2017-06-28 29 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()'的實現?我想,我還不明白這個論點。 –

相關問題