【C++】初始化列表構(gòu)造函數(shù)VS普通構(gòu)造函數(shù)
普通構(gòu)造函數(shù)VS初始化列表構(gòu)造函數(shù)
初始化列表構(gòu)造函數(shù)最優(yōu)先匹配問題
對于一個(gè)類而言,只要其中包含有初始化列表的構(gòu)造函數(shù),編譯器在編譯使用{}語法的構(gòu)造時(shí)會(huì)最傾向于調(diào)用初始化列表構(gòu)造函數(shù),哪怕做類型轉(zhuǎn)換也在所不惜,哪怕有類型最佳匹配的普通構(gòu)造函數(shù)或移動(dòng)構(gòu)造函數(shù)也會(huì)被劫持
class Widget {
public:
Widget(int i, bool b);
Widget(int i, double d);
Widget(std::initializer_list<long double> il);
operator float() const;
};
Widget w1(10, true); // 使?小括號初始化
//調(diào)?第?個(gè)構(gòu)造函數(shù)
Widget w2{10, true}; // 使?花括號初始化
// 調(diào)?第三個(gè)構(gòu)造函數(shù)
// (10 和 true 轉(zhuǎn)化為long double)
Widget w3(10, 5.0); // 使?小括號初始化
// 調(diào)?第二個(gè)構(gòu)造函數(shù)
Widget w4{10, 5.0}; // 使?花括號初始化
// 調(diào)?第三個(gè)構(gòu)造函數(shù)
// (10 和 5.0 轉(zhuǎn)化為long double)
Widget w5(w4); // 使?小括號,調(diào)?拷?構(gòu)造函數(shù)
Widget w6{w4}; // 使?花括號,調(diào)?std::initializer_list構(gòu)造函數(shù)
Widget w7(std::move(w4)); // 使?小括號,調(diào)?移動(dòng)構(gòu)造函數(shù)
Widget w8{std::move(w4)}; // 使?花括號,調(diào)?std::initializer_list構(gòu)造函數(shù)
編譯器這種熱衷于把括號初始化與初始化列表構(gòu)造函數(shù)匹配的行為,會(huì)導(dǎo)致一些莫名其妙的錯(cuò)誤
class Widget {
public:
Widget(int i, bool b);
Widget(int i, double d);
Widget(std::initializer_list<bool> il); // element type is now bool
… // no implicit conversion funcs
};
Widget w{10, 5.0}; //錯(cuò)誤!要求變窄轉(zhuǎn)換,int(10)double(5.0)無法轉(zhuǎn)換為bool類型
只有在沒有辦法把{}中實(shí)參的類型轉(zhuǎn)化為初始化列表時(shí),編譯器才會(huì)回到正常的函數(shù)決議流程中
?如我們在構(gòu)造函數(shù)中?std::initializer_list<std::string>代替std::initializer_list<bool> ,這時(shí)?std::initializer_list構(gòu)造函數(shù)將再次成為函數(shù)決議的候選者,因?yàn)闆]有辦法把int和bool轉(zhuǎn)換為std::string:
class Widget {
public:
Widget(int i, bool b);
Widget(int i, double d);
Widget(std::initializer_list<std::string> il);
…
};
Widget w1(10, true); // 使?小括號初始化,調(diào)?第?個(gè)構(gòu)造函數(shù)
Widget w2{10, true}; // 使?花括號初始化,調(diào)?第?個(gè)構(gòu)造函數(shù)
Widget w3(10, 5.0); // 使?小括號初始化,調(diào)?第?個(gè)構(gòu)造函數(shù)
Widget w4{10, 5.0}; // 使?花括號初始化,調(diào)?第?個(gè)構(gòu)造函數(shù)
{}空初始化列表會(huì)發(fā)生什么
假如{}內(nèi)是空的,類中既有默認(rèn)構(gòu)造函數(shù),也有初始化列表構(gòu)造函數(shù),此時(shí){}會(huì)被視為沒有實(shí)參,而不是一個(gè)空的初始化列表,因此會(huì)調(diào)用默認(rèn)構(gòu)造函數(shù)。如果就是想調(diào)用初始化列表構(gòu)造函數(shù),這應(yīng)該使用{{}}的方式
class Widget {
public:
Widget();
Widget(std::initializer_list<int> il);
...
};
Widget w1; // 調(diào)?默認(rèn)構(gòu)造函數(shù)
Widget w2{}; // 同上
Widget w3(); // 最令?頭疼的解析!聲明?個(gè)函數(shù)
Widget w4({}); // 調(diào)?std::initializer_list
Widget w5{{}}; // 同上
初始化列表帶來的vector的坑
std::vector<int> v1(10, 20); //使??std::initializer_list
//構(gòu)造函數(shù)創(chuàng)建?個(gè)包含10個(gè)元素的std::vector
//所有的元素的值都是20
std::vector<int> v2{10, 20}; //使?std::initializer_list
//構(gòu)造函數(shù)創(chuàng)建包含兩個(gè)元素的std::vector
//元素的值為10和20
初始化列表構(gòu)造函數(shù)問題帶來的兩點(diǎn)啟示
- 作為類庫作者,如果在構(gòu)造函數(shù)中重載了一個(gè)或多個(gè)初始化列表構(gòu)造函數(shù),要考慮用戶使用{}初始化的情況,最好避免類似std::vector中的情況。應(yīng)該盡可能做到用戶無論用小括號還是花括號進(jìn)行初始化都不會(huì)產(chǎn)生區(qū)別。一定要慎重考慮新出現(xiàn)的初始化列表構(gòu)造函數(shù)對其他構(gòu)造函數(shù)的影響!!!
- 作為類庫使用者,必須認(rèn)真的考慮小括號和花括號之間選擇創(chuàng)建對象的方式,最好選擇其中一個(gè)從一而終

浙公網(wǎng)安備 33010602011771號