28

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);

1T备注:除非不是数组,否则此函数不应参与重载决议。
2回报:unique_ptr<T>(new T(std::forward<Args>(args)...)).

template <class T> unique_ptr<T> make_unique(size_t n);

3T备注:除非是一个未知边界的数组,否则该函数不应参与重载决议。
4回报:unique_ptr<T>(new typename remove_extent<T>::type[n]()).

template <class T, class... Args> unspecified make_unique(Args&&...) = delete;

5备注:此函数不应参与重载决议,除非T是已知边界的数组。

现在,在我看来,这就像泥泞一样清晰,我认为它需要更多的阐述。但是,除了这篇社论评论,我相信我已经解码了每个变体的含义:

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

    make_unique您的非数组类型的沼泽标准。据推测,“备注”表示某种形式的静态断言或 SFINAE 技巧是为了防止模板在T为数组类型时被成功实例化。

    在高层次上,将其视为等效于 的智能指针T* ptr = new T(args);

  2. template <class T> unique_ptr<T> make_unique(size_t n);

    数组类型的变体。创建一个动态分配的n×数组Ts,并将其包装在 a 中返回unique_ptr<T[]>

    在高层次上,将其视为等效于 的智能指针T* ptr = new T[n];

  3. template <class T, class... Args> unspecified make_unique(Args&&...)

    不允许。“未指定”可能是unique_ptr<T[N]>

    否则将是智能指针等价于 invalid T[N]* ptr = new (keep_the_dimension_please) (the_dimension_is_constexpr) T[N];

首先,我说的对吗?如果是这样,第三个功能是怎么回事?

  • 如果它不允许程序员在为每个元素提供构造函数参数的同时尝试动态分配数组(这new int[5](args)是不可能的),那么这已经被第一个函数不能为数组类型实例化的事实所涵盖,不是吗?

  • T[N]* ptr = new T[N]如果它的存在是为了防止像(where Nis some )这样的结构添加到语言中,constexpr那么,为什么?包装一个动态分配的× s块的a 不是完全有可能unique_ptr<T[N]>存在吗?这会是一件坏事吗,以至于委员会已经竭尽全力禁止使用.NTmake_unique

为什么make_unique<T[N]>不允许?

4

2 回答 2

34

引用原始提案

T[N]

从 N3485 开始,unique_ptr不为T[N]. 但是,用户会很想写make_unique<T[N]>(). 这是一个双赢的局面。返回unique_ptr<T[N]>将选择单个对象的主要模板,这很奇怪。Returning将unique_ptr<T[]> 是 return 的铁定规则的一个例外 。因此,这个提议在这里形成了错误的格式,允许实现发出有用的消息。make_unique<something>()unique_ptr<something>T[N]static_assert

该提案的作者 Stephan T. Lavavej 在这段关于 Core C++ 的视频(由chris提供)中说明了这种情况,从 1:01:10 分钟开始(或多或少)。

于 2013-05-16T21:12:33.910 回答
0

对我来说,第三个重载看起来是多余的,因为并没有改变其他重载不匹配的事实,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 可以回答这个问题:)

于 2013-12-01T15:34:13.487 回答