【问题标题】:Is std::make_unique SFINAE-friendly?std::make_unique SFINAE 友好吗?
【发布时间】:2016-04-28 12:45:56
【问题描述】:

我正在做一些模板元编程,我想实现一个通用克隆函数,该函数通过 SFINAE(替换失败不是错误)根据表达式的有效性选择一种克隆方法。

this reference website 上面写着

功能

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

相当于:

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-friendly

如果T 不是可复制构造,则由于 SFINAE,后一个代码将不会参与重载决议。

下面是一个在 GCC 5.3 上编译失败且 C++14 开启的小例子:

#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() ); 
}

【问题讨论】:

  • 标准不保证,请勿依赖。
  • @Holt 标准不能保证什么?在 [unique.ptr.create] 它声明make_unique 返回unique_ptr&lt;T&gt;(new T(std::forward&lt;Args&gt;(args)...)).
  • @NathanOliver 是,但标准不保证如果 T 不能使用 Args&amp;&amp;... 构造,则 std::make_unique(Args&amp;&amp;...) 不应该存在。
  • @Holt 好吧。我只是不确定你在说什么。
  • 在您的情况下,您可以改用std::is_copy_constructible 特征。

标签: c++ c++11 unique-ptr sfinae


【解决方案1】:

该标准仅保证:

template <class T, class... Args> unique_ptr<T> std::make_unique(Args&&... args);

...必须返回unique_ptr&lt;T&gt;(new T(std::forward&lt;Args&gt;(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. 备注:除非 T 不是数组,否则此函数不应参与重载决议。
  2. 返回: unique_ptr&lt;T&gt;(new T(std::forward&lt;Args&gt;(args)...))

在您的情况下,您可能希望使用带有std::unique_ptr&lt;T&gt;(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);
}

【讨论】:

  • 一定会喜欢那个三重否定的:)
猜你喜欢
  • 2016-02-27
  • 1970-01-01
  • 2017-06-02
  • 2017-03-22
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2013-06-16
相关资源
最近更新 更多