簡體   English   中英

添加initializer_list構造函數后,靜默斷開構造函數調用

[英]Silent breaking of constructor calls after adding initializer_list constructor

我們考慮以下幾點:

#include <iostream>
#include <initializer_list>

class Foo {
public:
    Foo(int) {
        std::cout << "with int\n";
    }
};

int main() {
    Foo a{10};  // new style initialization
    Foo b(20);  // old style initialization
}

在運行它打印:

with int
with int

都好。 現在由於新的要求,我添加了一個構造函數,它帶有一個初始化列表。

Foo(std::initializer_list<int>) {
    std::cout << "with initializer list\n";
}

現在它打印:

with initializer list
with int

所以我的舊代碼Foo a{10}被默默地打破了。 a應該用int初始化。

我理解語言語法正在考慮將{10}作為包含一個項目的列表。 但是,如何防止舊代碼的這種無聲破壞呢?

  1. 是否有任何編譯器選項會在這種情況下向我們發出警告? 由於這將是編譯器特定的,我最感興趣的是gcc。 我已經嘗試過-Wall -Wextra
  2. 如果沒有這樣的選項,那么我們是否總是需要使用舊樣式構造,即使用() Foo b(20) ,對於其他構造函數,並且僅當我們真正意味着初始化列表時才使用{}

在這些情況下不可能生成任何警告,因為在直接匹配中選擇std::initializer_list構造函數的行為已明確定義並符合標准。

這個問題在Scott Meyers Effective Modern C ++ book Item 7中有詳細描述

但是,如果一個或多個構造函數聲明了std::initializer_list類型的參數,則使用支撐初始化語法的調用非常喜歡使用std :: initializer_lists的重載。 強烈。 如果編譯器有任何方法將使用支撐初始化程序的調用解釋為采用std::initializer_list的構造std::initializer_list ,則編譯器將采用該解釋。

他還提出了一些關於這個問題的邊緣案例,我強烈建議你閱讀它。

我找不到這樣的選項,所以顯然在這種情況下你應該使用括號用於具有initializer_list構造函數的類,並根據需要對所有其他類進行統一初始化。

一些有用的見解可以在這個答案和評論中找到: https//stackoverflow.com/a/18224556/2968646

沒有編譯器警告,也永遠不會。 警告代碼做一些常見的事情是沒有意義的

std::vector vec{1};

請記住,編譯器只會警告真正不需要的東西,比如未定義的行為。 它無法知道在上面的定義中,你的意思是調用構造函數來獲取一個size參數。 事實上,它知道你實際上想要一個帶有一個元素的向量! 它無法讀懂你的想法:)

你的第二個問題的答案基本上是肯定的。 您總是可以添加一個虛擬參數,如struct {} dummy; 避免使用帶有初始化列表的構造函數,但實際上,唯一相同的解決方案只是使用括號而不是大括號(或者不要突然斷開接口)。

如果要更改使用列表初始化的代碼的每個部分,可以刪除初始化列表構造函數,將它們更改為大括號, 然后正確實現構造函數。 我會認為這種改變是破壞性的,並適當地處理它。 另一個想法是事先想出初始化列表用例,並立即實現它。

暫無
暫無

聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.

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