代码之家  ›  专栏  ›  技术社区  ›  Jamboree

'std::array<T,0>'是否可以默认构造,其中'T'不可以默认构造?

  •  15
  • Jamboree  · 技术社区  · 7 年前

    #include <array>
    
    struct T
    {
        T() = delete;
    };
    
    int main()
    {
        std::array<T, 0> a;
        a.size();
    }
    

    我们默认初始化0大小的数组。因为没有元素,所以没有 T 应调用。

    然而 Clang 仍然需要 默认可构造,而 GCC 接受上述代码。

    std::array<T, 0> a{};
    

    Clang 这次接受了。

    T std::array<T, 0> 从默认可构造开始?

    3 回复  |  直到 7 年前
        1
  •  5
  •   StoryTeller - Unslander Monica    7 年前

    因为没有元素,所以不应该调用T的构造函数。
    非默认可构造T是否阻止 std::array<T, 0>

    我们应该回答这个问题。零大小阵列专门化的行为如下所示:

    [array.zero]

    1阵列应为特殊情况N==0提供支持。
    2在N==0的情况下,begin()==end()==唯一值。data()的返回值未指定。

    4成员函数swap()应具有非抛出异常规范。

        2
  •  5
  •   Jamboree    7 年前

    多亏了@T.C.,正如在他的 comment ,地址为 LWG 2157

    拟议的决议增加了这一要点(重点是我的):

    array<T, 0> a = { };
    

    并且所述初始化必须有效 即使T不是默认可构造的

    所以很明显,预期的行为是 std::array<T, 0> 即使T不是,也可以构造默认值。

        3
  •  2
  •   Curious    7 年前

    这个问题解释了clang和 std::array Deleted default constructor. Objects can still be created... sometimes

    但是有 gcc 不同之处在于库代码。gcc代码库中确实有一个与这个问题相关的具体实现细节 @StoryTeller mentioned

    gcc有一个特殊情况 如果大小为0,请从其 <array> 标题(来自gcc 5.4.0 )

    template<typename _Tp, std::size_t _Nm>
    struct __array_traits
    {
      typedef _Tp _Type[_Nm];
    
      static constexpr _Tp&
      _S_ref(const _Type& __t, std::size_t __n) noexcept
      { return const_cast<_Tp&>(__t[__n]); }
    
      static constexpr _Tp*
      _S_ptr(const _Type& __t) noexcept
      { return const_cast<_Tp*>(__t); }
    };
    
    template<typename _Tp>
    struct __array_traits<_Tp, 0>
    {
     struct _Type { };
    
     static constexpr _Tp&
     _S_ref(const _Type&, std::size_t) noexcept
     { return *static_cast<_Tp*>(nullptr); }
    
     static constexpr _Tp*
     _S_ptr(const _Type&) noexcept
     { return nullptr; }
    };
    

    正如你所见,有一个专业化的 __array_traits std::数组 对于底层数组),当数组大小为0时,它甚至没有模板化的类型的数组 _Type 不是数组,而是空结构!

    这就是为什么没有调用构造函数。