在重載的時候摧冀,vector會有問題倍踪。
當需要可變參數(shù),如果使用vector
的話索昂,可能會遇到下面這個問題建车。函數(shù)f
有兩個重載的版本,編譯器無法選擇具體調(diào)用vector
還是list
的版本椒惨。
void f(std::vector<int> const &items){};
void f(std::list<int> const &items){};
f({ 1, 2, 3, 4 }); //ambiguous call to overloaded function
而使用initializer_list
的話缤至,就不會出現(xiàn)錯誤了。編譯器優(yōu)先匹配了initializer_list
的版本康谆。
void g(std::vector<int> const &items){};
void g(std::list<int> const &items){};
void g(std::initializer_list<int> const &items){};
g({ 1, 2, 3, 4 }); // no error
initializer_list不能修改领斥,更符合參數(shù)的特點。
vector有push_back函數(shù)沃暗,也就是說vector可以在函數(shù)里面修改月洛,所以必然vector必須在heap上分配空間來存儲數(shù)據(jù)。
而initializer_list
只有begin
和end
函數(shù)描睦,函數(shù)內(nèi)并不能修改它膊存,所以編譯器有機會在stack上存儲initializer_list
的數(shù)據(jù)來提高性能导而。
initializer_list has pointer semantics while the vector has value semantics.
vector
是值語義忱叭,也就是說拷貝一個vector
隔崎,那里面的元素也會被拷貝一次。而initializer_list
是指針語義韵丑,里面的元素并不會被拷貝爵卒。比如說下面這段代碼list
和list2
的begin
其實指向了同一個空間。這樣的設計是合理的撵彻,因為initializer_list
是不可修改的钓株,沒有理由再拷貝一次。
std::initializer_list<int> list = { 1, 2, 3, 4 };
std::initializer_list<int> list2;
list2 = list;
std::cout << list2.begin() << std::endl;
std::cout << list.begin() << std::endl;
指針語義的好處是陌僵,下面這段遞歸函數(shù)不會對里面的元素產(chǎn)生很多次復制轴合。雖然每次都構(gòu)造了一個新的initializer_list
,但是里面的數(shù)值{ 1, 2, 3, 4, 5, 6, 7, 8, 9, 10 }
并沒有經(jīng)過復制碗短。
int sum(std::initializer_list<int> const &items)
{
std::cout << items.begin() << std::endl;
if (items.begin() == items.end()){
return 0;
}
std::initializer_list<int> next(items.begin() + 1, items.end());
return *(items.begin()) + sum(next);
};
std::cout << sum({ 1, 2, 3, 4, 5, 6, 7, 8, 9, 10 });
initializer_list 背后的設計思想受葛。
在C++11的時候,大家都想加上一個值列表的東西偎谁,就像{ value1, value2, value2... valueN }
一樣总滩。一種想法是搞出一個新的關(guān)鍵字,給C++增加一個新的build-in類型巡雨。但是新的關(guān)鍵字很有可能會導致老的程序無法被編譯闰渔,如果湊巧老的程序使用了那個關(guān)鍵字做名字。
于是C++11的做法是铐望,只是在標準模板庫里面增加一個新的模板initializer_list
冈涧,然后讓編譯器遇到{1,2,3,4}
這種東西的時候,隱式的轉(zhuǎn)換成一個initializer_list
的對象正蛙。下面這段代碼輸出的是class std::initializer_list<int>
auto list = { 1, 2, 3 };
std::cout << typeid(decltype(list)).name() << std::endl;
有了這個基本的東西以后炕舵,剩下的問題就可以在已有的框架里面解決了。比如說實現(xiàn)std::vector<int> v = { 1, 2, 3 };
這個功能跟畅。其實就是編譯器遇到{ 1, 2, 3 }
就生成了一個initializer_list
咽筋,然后調(diào)用了vector對應的一個構(gòu)造函數(shù).
vector( std::initializer_list<T> init, const Allocator& alloc = Allocator() );
回到最初的f
和g
的例子,g({ 1, 2, 3, 4 });
沒有編譯錯誤徊件,因為有一個最佳的匹配奸攻。
f({ 1, 2, 3, 4 });
出現(xiàn)了編譯錯誤,因為沒有最佳的一個匹配虱痕,編譯器面臨著隱式類型轉(zhuǎn)換睹耐,但是有兩個選擇,vector和list部翘,所以就有編譯錯誤了硝训。