C++ 为什么“make_”是独一无二的<;T[N]>;`不允许?

C++ 为什么“make_”是独一无二的<;T[N]>;`不允许?,c++,language-lawyer,unique-ptr,c++14,C++,Language Lawyer,Unique Ptr,C++14,假设整个命名空间std C++14委员会草案N3690定义了std::make_unique: [n3690:20.9.1.4]:unique\u ptr创建[unique.ptr.create] template unique\u ptr make\u unique(Args&&…Args) 1备注:除非T不是数组,否则此函数不得参与重载解析。 2返回:unique\u ptr(新的T(std::forward(args)…)。 模板唯一性\u ptr使唯一性(大小\u t n) 3备注:除非

假设整个命名空间
std

C++14委员会草案N3690定义了
std::make_unique

[n3690:20.9.1.4]:
unique\u ptr
创建
[unique.ptr.create]

template unique\u ptr make\u unique(Args&&…Args)

1备注:除非
T
不是数组,否则此函数不得参与重载解析。
2返回:
unique\u ptr(新的T(std::forward(args)…)。

模板唯一性\u ptr使唯一性(大小\u t n)

3备注:除非
T
是未知边界的数组,否则此函数不应参与重载解析。
4返回:
unique\u ptr(新的typename-remove\u-extent::type[n]())。

模板未指定使_唯一(Args&&…)=delete

5备注:除非
T
是已知边界的数组,否则此函数不得参与重载解析

现在,在我看来,这似乎像泥一样清晰,我认为它需要更多的解释。但是,撇开这篇社论评论不谈,我相信我已经破解了每个变体的含义:

  • template unique\u ptr make\u unique(Args&&…Args)

    对于非数组类型,您的bog标准
    使_唯一
    。“备注”可能表示,当
    T
    是数组类型时,某种形式的静态断言或SFINAE技巧会阻止模板成功实例化

    在高层,将其视为相当于
    T*ptr=newt(args)的智能指针

  • 模板唯一性\u ptr使唯一性(大小\u t n)

    数组类型的变体。创建动态分配的
    n
    ×
    Ts
    数组,并将其包装在
    唯一的\u ptr
    中返回

    在高层,将其视为相当于
    T*ptr=newt[n]的智能指针

  • 模板未指定使_唯一(Args&&…

    不允许。“未指定”可能是唯一的

    否则,智能指针将相当于无效的
    T[N]*ptr=new(请保留维度)(维度是常量)T[N]

  • 首先,我说的对吗?如果是这样的话,第三个函数是怎么回事

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

    • 如果它是为了防止像
      T[N]*ptr=newt[N]
      (其中
      N
      是一些
      constexpr
      )这样的构造添加到语言中,那么,为什么呢?难道不完全可能存在一个
      唯一的\u ptr
      来包装动态分配的
      N
      ×
      t
      s块吗?这会不会是一件坏事,以至于委员会特意禁止使用
      make_unique

    为什么不允许
    使你独一无二?

    引用:

    T[N]

    从N3485开始,
    unique\u ptr
    没有为
    t[N]
    提供部分专门化。 但是,用户会强烈地倾向于编写
    make_unique()
    。这 这是一个不可能赢的局面。返回
    unique\u ptr
    将选择主 单个对象的模板,这很奇怪。返回
    unique\u ptr
    将是一个例外,否则铁的规则
    make_unique()
    返回
    unique_ptr
    。因此, 提案使得这里的
    T[N]
    格式错误,允许实现发出 有用的
    静态\u断言
    消息


    提案的作者Stephan T.Lavavej(由提供)从1:01:10分钟(或多或少)开始说明了这种情况。

    对我来说,第三个重载看起来是多余的,因为它不会改变其他重载不匹配的事实,而且它似乎无助于生成更好的错误消息。考虑以下实现:

    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 ]() );
    }
    
    模板
    typename启用\u如果<!是数组:值,唯一的\u ptr:type
    使_唯一(Args&&…Args)
    {
    返回唯一的(新的T(向前的(Args)…);
    }
    模板
    如果::值和范围::值==0,则启用类型名,唯一的\u ptr::类型
    使唯一(常数大小)
    {
    使用U=typename删除\U区段::type;
    返回唯一的(新的U[n]());
    }
    
    当您尝试调用
    std::make_unique(1)
    时,错误消息将两个候选项都列为已被
    enable_禁用(如果
    )。如果添加第三个已删除的重载,则错误消息将列出三个候选项。另外,由于它被指定为
    =delete
    ,您不能在第三个重载的主体中提供更有意义的错误消息,例如,
    static_assert(sizeof(T)==0,“std::make_shared不允许已知绑定的数组”)

    这是你想玩的箱子


    第三个重载最终出现在N3656和N3797中的事实可能是由于
    make_unique
    的发展历史,但我想只有STL可以回答:)

    “除非T不是数组,否则此函数不应参与重载解析。”-是的……我认为答案是@Mehrdad:不是草稿,但是一个新的标准…@Mehrdad:我同意!幸运的是,你的建议很容易遵循,因为到目前为止还没有人这么做