是`std :: array <t,0 =“”>`默认是可构造的,其中`T`不是默认可构造的?

时间:2017-06-28 10:24:44

标签: c++ arrays c++11 language-lawyer

请考虑以下代码:

#include <array>

struct T
{
    T() = delete;
};

int main()
{
    std::array<T, 0> a;
    a.size();
}

我们默认初始化一个0大小的数组。由于没有元素,因此不应调用T的构造函数。

但是,Clang仍然需要T作为默认构造,而GCC接受上述代码。

请注意,如果我们将数组初始化更改为:

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

Clang这次接受了。

非默认可构造T是否会阻止std::array<T, 0>进行默认构造?

3 个答案:

答案 0 :(得分:5)

  

由于没有元素,因此不应该调用T的构造函数   非默认可构造的T是否会阻止std::array<T, 0>成为默认构造?

标准没有指定布局std::array<T, 0>应该让我们回答这个问题。零大小的数组专门化只表现为如下:

[array.zero]

  

1个数组应为特殊情况N == 0提供支持   2在N == 0的情况下,begin()== end()==唯一值。 data()的返回值未指定   3对于零大小的数组调用front()或back()的效果是不确定的   4成员函数swap()应具有非抛出异常规范。

您注意到的行为很可能是由于实施方面的差异。

答案 1 :(得分:5)

感谢@ T.C。,正如他在comment中指出的那样,它在LWG 2157中得到了解决,在撰写本文时仍然是一个未解决的问题。

建议的决议增加了这个要点(强调我的):

  

此情况下未指定的数组内部结构应允许初始化,如:

array<T, 0> a = { };
     

并且表示初始化必须有效即使T不是默认构造

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

答案 2 :(得分:2)

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

会发生什么

但是使用gcc,差异来自库代码。 gcc代码库中确实存在与此问题相关的具体实现细节@StoryTeller mentioned

gcc有一个std::array的特殊情况,大小为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; }
};

正如您所看到的,当数组大小为0时,__array_traits(在std::array中用于底层数组)有一个特化,甚至没有一个数组键入它的模板。类型_Type不是数组,而是空结构!

这就是没有调用构造函数的原因。