[英]Default construction of deleted constructor with braced initializer list
假設我想禁用類的構造,那么我可以執行以下操作(根據最佳樣式刪除所有構造函數(或其他函數)? ):
// This results in Example being CopyConstructible:
struct Example {
Example() = delete;
};
要么
struct Example {
template <typename... Ts>
Example(Ts&&...) = delete;
};
要么
struct Example {
Example(const Example&) = delete;
};
如果構造了第一個例子仍然可以被復制(意圖是禁用),但是后兩個將禁用幾乎所有創建Example
方法。 如果我使用空的括號初始化列表默認構造上述任何一個,則成功構造實例。 在Meyer的Effective Modern C ++中,他給出了示例(第51頁底部):
Widget w3{}; // calls Widget ctor with no args
因此,我希望上述Example
類失敗,即:
Example e{};
不應該構造,因為它應該調用已刪除的默認構造函數。 但是,它確實可用,如果定義為上面的第一種情況,也是可復制的。 查看現場演示 。 我的問題是:這是正確的,如果是的話,為什么? 另外,如果這是正確的,我如何完全禁用類的銷毀?
我們將首先闡明初始化對象的含義以及如何/何時/是否調用構造函數。
以下是我的外行人對標准的解釋,為簡單起見,一些不相關的細節被忽略或破壞。
初始化程序是以下之一
() // parentheses
// nothing
{} // braced initializer list
= expr // assignment expression
括號和括號初始值設定項列表可能包含更多表達式。
給定struct S
,它們就像這樣使用
new S() // empty parentheses
S s(1, 2) // parentheses with expression list as (1, 2)
S s // nothing
S s{} // empty braced initializer list
S s{{1}, {2}} // braced initializer list with sublists
S s = 1 // assignment
S s = {1, 2} // assignment with braced initializer list
請注意,我們還沒有提到構造函數
根據使用的初始化器執行初始化。
new S() // value-initialize
S s(1, 2) // direct-initialize
S s // default-initialize
S s{} // list-initialize
S s{{1}, {2}} // list-initialize
S s = 1 // copy-initialize
S s = {1, 2} // list-initialize
一旦執行初始化,就認為該對象已初始化。
請注意,同樣沒有提到構造函數
我們將主要解釋列出初始化內容的含義,因為這是手頭的問題。
發生列表初始化時,將按順序考慮以下內容
聚合類型定義為[dcl.init.aggr]
聚合是一個數組或類
- 沒有用戶提供的,顯式的或繼承的構造函數
- 沒有私有或受保護的非靜態數據成員
- 沒有虛函數,也沒有虛擬,私有或受保護的基類
刪除構造函數不計入提供構造函數。
聚合的元素定義為
聚合的元素是:
- 對於數組,數組元素按下標順序遞增,或者
- 對於類,聲明順序中的直接基類,后跟聲明順序中不是匿名聯合的成員的直接非靜態數據成員。
聚合初始化定義為
[...]初始化列表的元素按順序作為聚合元素的初始值設定項。
Example e{}
遵循上述規則,為什么Example e{}
是合法的是因為
the initializer is a braced initializer list
uses list initialization
since Example is an aggregate type
uses aggregate initialization
and therefore does not invoke any constructor
當您編寫Example e{}
,它不是默認構造的。 它是聚合初始化的。 所以,是的,它完全沒問題。
實際上,以下編譯
struct S
{
S() = delete;
S(const S&) = delete;
S(S&&) = delete;
S& operator=(const S&) = delete;
};
S s{}; //perfectly legal
確保Example
不是聚合類型以停止聚合初始化並刪除其構造函數。
這通常是微不足道的,因為大多數類都有私有或受保護的數據成員。 因此,經常忘記C ++中存在聚合初始化。
使類非聚合的最簡單方法是
struct S
{
explicit S() = delete;
};
S s{}; //illegal, calls deleted default constructor
但是,截至2017年5月30日,只有gcc 6.1及以上版本和clang 4.0.0將拒絕這一點,所有版本的CL和icc都會錯誤地接受這一點。
這是在C ++中最瘋狂的一個角落,這是
地獄般的
信息看,通過標准來了解到底發生了什么。 已經寫了很多參考文獻 ,我不會試圖解釋它們。
聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.