假设命名空间为std
。
c++ 14委员会草案N3690这样定义std::make_unique
:
[n3690: 20.9.1.4]:
unique_ptr
创建,,, [unique.ptr.create]
template <class T, class... Args> unique_ptr<T> make_unique(Args&&... args);
1备注:除非
T
不是数组,否则此函数不参与重载解析。
2返回:unique_ptr<T>(new T(std::forward<Args>(args)...)).
template <class T> unique_ptr<T> make_unique(size_t n);
3备注:除非
T
是一个边界未知的数组,否则此函数不参与重载解析。
4返回:unique_ptr<T>(new typename remove_extent<T>::type[n]()).
template <class T, class... Args> unspecified make_unique(Args&&...) = delete;
5备注:除非
T
是已知界的数组,否则此函数不参与重载解析。
现在,这对我来说似乎就像泥一样清晰,我认为它需要更多的解释。但是,撇开这篇社论的评论不谈,我相信我已经破译了每个变体的含义:
-
template <class T, class... Args> unique_ptr<T> make_unique(Args&&... args);
非数组类型的标准
make_unique
。据推测,"remark"表示某种形式的静态断言或SFINAE技巧是为了防止模板在T
是数组类型时成功实例化。在高层次上,把它看作相当于
T* ptr = new T(args);
的智能指针。 -
template <class T> unique_ptr<T> make_unique(size_t n);
数组类型的变体。创建一个动态分配的数组
n
×Ts
,然后用unique_ptr<T[]>
封装返回。在高层次上,把它看作相当于
T* ptr = new T[n];
的智能指针。 -
template <class T, class... Args> unspecified make_unique(Args&&...)
不允许。"未指定"可能是
unique_ptr<T[N]>
。在其他情况下等同于无效的
T[N]* ptr = new (keep_the_dimension_please) (the_dimension_is_constexpr) T[N];
。
首先,我对吗?如果是这样,第三个函数是什么?
-
如果它不允许程序员在为每个元素提供构造函数参数的同时尝试动态分配数组(就像
new int[5](args)
是不可能的),那么这已经被第一个函数不能为数组类型实例化的事实所覆盖,不是吗? -
如果它是为了防止像
T[N]* ptr = new T[N]
这样的结构添加到语言中(其中N
是一些constexpr
),那么,为什么呢?难道不完全可能存在一个unique_ptr<T[N]>
来包装一个动态分配的N
×T
年代?这会是一件很糟糕的事情吗,以至于委员会已经不遗余力地禁止使用make_unique
来创建它?
为什么不允许make_unique<T[N]>
?
引自原提案:
T[N]
从N3485开始,
unique_ptr
不提供T[N]
的部分专门化。然而,用户会强烈地想写make_unique<T[N]>()
。这是一个没有赢家的场景。返回unique_ptr<T[N]>
将选择主节点单个对象的模板,这很奇怪。返回unique_ptr<T[]>
会不会是铁定规则的例外呢make_unique<something>()
返回unique_ptr<something>
。因此,该提案使得这里的T[N]
格式错误,允许实现发出有用的static_assert
消息。
该提案的作者Stephan T. Lavavej在Core c++的视频中说明了这种情况(由chris提供),从1分01:10分开始(或多或少)
对我来说,第三个重载看起来是多余的,因为它并没有改变其他重载不匹配T[N]
的事实,而且不似乎有助于生成更好的错误消息。考虑以下实现:
template< typename T, typename... Args >
typename enable_if< !is_array< T >::value, unique_ptr< T > >::type
make_unique( Args&&... args )
{
return unique_ptr< T >( new T( forward< Args >( args )... ) );
}
template< typename T >
typename enable_if< is_array< T >::value && extent< T >::value == 0, unique_ptr< T > >::type
make_unique( const size_t n )
{
using U = typename remove_extent< T >::type;
return unique_ptr< T >( new U[ n ]() );
}
当您尝试调用std::make_unique<int[1]>(1)
时,错误消息将enable_if
禁用了两个候选项。如果添加第三个已删除的重载,则错误消息会列出三个候选重载。此外,由于它被指定为=delete;
,因此您不能在第三个重载的主体中提供更有意义的错误消息,例如static_assert(sizeof(T)==0,"array of known bound not allowed for std::make_shared");
。
这里是的实时示例,如果你想玩它。
第三次重载在N3656和N3797中结束的事实可能是由于make_unique
随着时间的推移而发展的历史,但我想只有STL才能回答这个问题:)