簡體   English   中英

具有支撐初始化列表的已刪除構造函數的默認構造

[英]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

一旦執行初始化,就認為該對象已初始化。

請注意,同樣沒有提到構造函數

列表初始化

我們將主要解釋列出初始化內容的含義,因為這是手頭的問題。

發生列表初始化時,將按順序考慮以下內容

  1. 如果對象是聚合類型,並且列表具有單個元素,該元素是對象的類型或從對象的類型派生,則使用該元素初始化對象
  2. 如果對象是聚合類型,則對象將進行聚合初始化
  3. 如果列表為空,並且對象具有默認構造函數,則對象將進行值初始化(最終調用默認構造函數)
  4. 如果對象是類類型,則考慮構造函數,使用列表的元素執行重載解析

骨料

聚合類型定義為[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.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM