2017-06-28 24 views
14

考虑下面的代码:`std :: array <T, 0>`default constructible'T`不是默认可构造的吗?

#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>被默认构造?

+2

即使有删除的默认构造函数,聚合初始化也是有效的。但是默认初始化(iirc)不是。我认为clangs的行为是合理的。执行或不执行不影响编译时代码是否有效。 –

+1

@ JohannesSchaub-litb你期望一个元素被初始化为一个0大小的数组吗?锵做[that](https://wandbox.org/permlink/LashAGkeVwnfsodC),这看起来很疯狂。如果没有元素,则不应初始化元素。 – Jamboree

+2

参见[LWG 2157](https://timsong-cpp.github.io/lwg-issues/2157)。 –

回答

3

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

的决议建议增加这子弹点(重点煤矿):

阵列的这种情况下,未指定的内部结构应允许像初始化:

array<T, 0> a = { }; 

和所说的初始化必须有效即使T不是默认构造的

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

+0

很明显,这就是提议者(和JW)现在想要的行为,但不一定是其他人,也不一定是在编译器版本被编写/发布时!事实上,你所链接的文本表明,原意恰恰相反。尽管如此,这仍然是最好的答案。 –

2

这个问题解释铿锵发生了什么和std::arrayDeleted default constructor. Objects can still be created... sometimes

但随着gcc的差异来自于库代码。的确有在GCC代码库的具体实施细节,是有关这个问题的@StoryTeller mentioned

GCC有std::array大小为0的特殊情况,请参见下面的代码(从GCC 5.4.0

他们 <array>
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::array用于底层数组)当阵列大小为0,即甚至没有它的模板上的类型的阵列。类型_Type不是一个数组,而是一个空的结构!

这就是为什么没有构造函数被调用的原因。

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()应该有一个非抛出异常规范。

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

+0

这不仅是关于布局。 Clang的实现实际上是在运行时为0大小的数组初始化一个元素。 – Jamboree

+1

@Jamboree - 因为他们没有被绑定到任何特定的布局。他们的标准下的特权。 – StoryTeller

+1

如果参数是“这个列表不禁止创建一个元素,那么允许创建一个元素”,您是否同意为'array a;'调用'std :: exit()'的实现?我想,我还不明白这个论点。 –

相关问题