std::make_unique是否支持SFINAE?

3
我是一位有用的助手,可以为您翻译文本。
我正在进行一些模板元编程,并希望实现一个通用克隆函数,根据SFINAE(替换失败不是错误)中的表达式有效性选择克隆方法。
这个参考网站上,它说:

该函数

make_unique<T>( std::forward<Args>(args)... )

is equivalent to:

unique_ptr<T>(new T(std::forward<Args>(args)...))
这是否意味着以下代码:
template <typename T>
auto my_clone( const T & t ) -> decltype( std::make_unique<T>(t) )
{
    return std::make_unique<T>(t);
}

应该完全等同于
template <typename T>
auto my_clone( const T & t ) -> decltype( std::unique_ptr<T>( new T(t) ) )
{
    return std::unique_ptr<T>( new T(t) );
}

即使我有其他的my_clone函数重载, std::make_unique()在语义上是否符合SFINAE?换句话说: std::make_unique()SFINAE友好型的吗?
如果T不可复制构造,则由于SFINAE,后一个代码将不参与重载决策。
这是一个小例子,在启用C++14的GCC 5.3上编译失败:
#include <memory>

// It does **not** work with this snippet:
template <typename T>
auto my_clone( const T & t ) -> decltype( std::make_unique<T>( t ) )
{
    return std::make_unique<T>( t );
}

/* // But it works with this snippet instead:
template <typename T>
auto my_clone( const T & t ) -> decltype( std::unique_ptr<T>( new T(t) ) )
{
    return std::unique_ptr<T>( new T(t) );
}*/

// This is another overload for testing purposes.
template <typename T>
auto my_clone( const T & t ) -> decltype(t.clone())
{
    return t.clone();
}  

class X
{
public:
    X() = default;

    auto clone() const
    {
        return std::unique_ptr<X>( new X(*this) );
    }

private:
    X( const X & ) = default;
}; 

int main()
{
    // The following line produces the compiler error: 
    // "call to 'my_clone' is ambiguous"
    const auto x_ptr = my_clone( X() ); 
}

1
标准并不保证它,因此您不应该依赖它。 - Holt
1
@Holt 标准没有保证什么?在 [unique.ptr.create] 中它声明 make_unique 返回unique_ptr<T>(new T(std::forward<Args>(args)...)) - NathanOliver
1
@NathanOliver 是的,但标准并不保证如果T不能使用Args&&...构造,则std::make_unique(Args&&...)不存在。 - Holt
@Holt 哦,好的。我只是不确定你具体在说什么。 - NathanOliver
1
在您的情况下,您可以使用 std::is_copy_constructible 特性。 - Jarod42
1个回答

6
标准仅保证以下内容:
template <class T, class... Args> unique_ptr<T> std::make_unique(Args&&... args);

...必须返回unique_ptr<T>(new T(std::forward<Args>(args)...)),但这并不保证make_unique函数只有在T使用Args...可构造时才存在,因此它不符合SFINAE(按标准),因此您不能依赖它。

标准中唯一提到make_unique的部分:

§20.8.1.4 [unique.ptr.create]:

template <class T, class... Args> unique_ptr<T> make_unique(Args&&... args);
  1. Remarks: This function shall not participate in overload resolution unless T is not an array.
  2. Returns: unique_ptr<T>(new T(std::forward<Args>(args)...)).
在您的情况下,您可以选择使用带有std::unique_ptr<T>(new T(...))版本或使用is_copy_constructible使您的my_clone SFINAE友好(@Yakk,@Jarod42),例如:
template <typename T,
          typename = std::enable_if_t<std::is_copy_constructible<T>::value>>
auto my_clone(const T & t) -> decltype(std::make_unique<T>(t)) {
    return std::make_unique<T>(t);
}

1
一定要喜欢那个三重否定 :) - T.C.

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接