[英]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}
作為包含一個項目的列表。 但是,如何防止舊代碼的這種無聲破壞呢?
-Wall -Wextra
。 ()
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.