c++11后引入了uniform initialization的概念,按照它說的,任何的初始化操作都可以借由大括號{}搞定。
比如在c++98時代,我們會用:
int i[] = {1,3,4,5,6}; //初始化數組
class Cat
{
public:
Cat(int old, int weight):old(old),weight(weight){};
int old, weight;
};
Cat c1(12,32); //初始化類實例
Dog d1= 13; // 參數化式地初始化類實例(單參數)
Dog d2 = d1; // 拷貝構造函數初始化類示例
總之,初始化不同的東西,有各種各樣的語法,c++11干脆把所有的初始化都用{}實現吧。
int i[]{1,2,3,4};
Cat c1{12,32};
// Dog類似
Dog d1{d3};
Dog d2{d1};
坑,大坑
似乎一切歲月靜好,無腦用大括號就完事了。但考慮下面這樣的情況:
vector<int> vi{2,3}; //執行完這句,vi里是2,3還是3,3呢?
c++98的程序員知道vector有個構造函數是這樣的:
vector( size_type count, const T& value, const Allocator& alloc = Allocator());
就是說初始化時填入count個value。
那么按照大括號里的東西就是構造函數的參數的邏輯,vi構造完的結果是3,3。
但是實際上是2,3。太坑了,太坑了。
如果我們想弄出3,3。得寫成
vector<int> vi(2,3);
怎么一會兒大括號里是構造函數的參數,一會兒又不是,煩死了。
解釋
具體原因就是編譯器看到被大括號包起來的東西,會將其轉化成initializer_list
{1,2,3} --------> initializer_list<int>
即編譯器見到前者,就會把其轉換成后者。
如果類沒有以initializer_list
{}和
()沒有任何區別,該調用哪個調用哪個,直接將
{}看成
()就完事。
但是,如果類有initializer_list
重點來了,為什么vector
373 vector(initializer_list<value_type> __l,
374 const allocator_type& __a = allocator_type())
375 : _Base(__a)
376 {
377 _M_range_initialize(__l.begin(), __l.end(),
378 random_access_iterator_tag());
379 }
既然有這個構造函數,遇見{}時就該調用它,把initializer_list一整包丟進去。而非調用像上面的
vector( size_type count, const T& value, const Allocator& alloc = Allocator());
相關知識
c++11中{}語法的出現,也導致了多實參非explicit ctor構造函數可能會在隱式轉換時調用。在以前,只有one argument non-explicit ctor才有可能會在隱式轉換時調用,因此我們加個explicit就阻止了這種轉換。但是c++11后編譯器會將{}自動解包(如果沒有initializer_list為參的構造函數時),可能解包出來多個東西。因此將multi-argument ctor前面加上explicit就變得有必要了。
一些bb
我覺得這個設計有些問題,當用第三方類庫的時候,鬼知道它有沒有寫initializer_list為參的構造函數。本來我只是想開開心心用{}的語法調用普通構造函數,結果不小心調用了initializer_list的版本,佛了。感情我寫{}之前還得看看類庫的源代碼是吧。
最后我想出了一個解決辦法,估計也是設計者想讓我們用的吧,就是正常創建類對象時,用()就完事。只有真正當我知道該類(例如stl容器)提供了初始化列表的版本,並且我知道其行為,並且我真的需要它的時候,我才用{}。
