主要內容
{}
好處
可以在幾乎所有的場景使用的初始化語法.
規避語法:
int a()
,實際是成員函數聲明,編譯器優先這樣解析的.可以寫
c++98
不支持的語法.內容
優先匹配:
initialize
,成員函數,成員變量.
統一初始化
歷史
- 以前的初始化方式千奇百怪.
C++11
之後編譯器創建了{}
,可以支持各種環境下的初始化.案例
int main() { int a = 0; int b(1); int c{1}; int d = {1}; }
分析
c,d
的方式一樣,後面就介紹c
.
賦值和構造
案例
#include <iostream> class D { public: D() {std::cout << __LINE__ << std::endl;} D(const D& d) {std::cout << __LINE__ << std::endl;} void operator=(const D& d){std::cout << __LINE__ << std::endl;} }; int main() { D a; D b = a; b = a; return 0; }
分析
D b = a
是用拷貝構造調用,後面的則是賦值構造.新手一臉懵逼.
容器默認值
案例
#include <vector> int main() { std::vector<int> v {1,2,3,4,5}; return 0; }
gcc test.cpp -std=c++98
編譯不過
C++11
支持了std::initializer_list
類型,語法上也進行了支持.統一初始化和
{}
初始化.
統一初始化時俗語.方法.
{}
胡括號初始化是C++
的語法.
成員函數默認值
案例
class T{ private: int a{0}; int b = 0; int c(0); }; int main(){ }
分析
c++
在語法上不支持.
有拷貝構造和無拷貝構造
案例
class T{ public: T(int){} T(const T&) = delete; }; int main(){ T a {0}; T b (0); T c = 0; }
分析
無拷貝構造不允許
c
方式的構造.有才允許,語法上的問題.
窄化
案例
int main(){ int a(1.0 + 2); int b{1 + 2}; int c{1.0 + 2}; }
分析
窄化: 浮點和非浮點之間轉換,賦值溢出.
()
默認不管,{}
編譯不通過,語法支持,如果()
也支持會有很大的問題.根據
ocp
原則,增可以,改動不可以.兼容性會很差.
聲明和定義
案例
int main(){ int c(); int b = c; }
分析
c
是函數類型,這是編譯器支持,將具有二義性的看成聲明.這裏就是函數的聲明.使用
{}
可以規避,因爲語法上支持這種,沒有二義性.
小結
前面介紹了優點.下面介紹一下缺點.
主要從:
{}
初始化,std::initializer_lists
,和構造函數重載.行爲看起來像是在幹一件事兒,其實各幹各的.
構造無
std::initializer_lists
,(){}
乾的一樣的事情.
auto
案例
#include <initializer_list> template <typename T> void show(T&& d) { d.error(); } int main() { auto s = {1,2,3,4}; show(s); }
分析
默認推導出來的是
std::initializer_list<int>
.兩者之間有衝突.
無std::initializer_lists
的(){}
說明
- 兩者是一個意思.
有std::initializer_lists
案例
#include <iostream> #include <initializer_list> class T { public: T(long,double){std::cout<<__LINE__<<std::endl;} T(std::initializer_list<bool>){std::cout<<__LINE__<<std::endl;} }; int main() { T s{1,2.0}; }
分析
能匹配
initializer_list
就一定匹配轉換.即使進行多個步驟.
窄化
非窄化
#include <iostream> #include <initializer_list> class T { public: T(long,double){std::cout<<__LINE__<< ":dd" <<std::endl;} T(std::initializer_list<bool>){std::cout<<__LINE__ << ":ss" <<std::endl;} }; int main() { T s{1.0,2.0}; T s{1,2.0}; }
分析
浮點轉
bool
編譯器不會報錯,浮點轉非浮點會.
無法轉化std::initializer_list
案例
#include <iostream> #include <string> #include <initializer_list> class T { public: T(long,double){std::cout<<__LINE__<< ":dd" <<std::endl;} T(std::initializer_list<std::string>){std::cout<<__LINE__ << ":ss" <<std::endl;} }; int main() { T a{1,2.0}; T b{1.0,2.0}; }
分析
無法轉化爲
std::string
類型,所以恢復構造並檢驗是否窄化.
默認構造還是std::initializer_list
案例
#include <iostream> #include <string> #include <initializer_list> class T { public: T(){std::cout<<__LINE__<< ":dd" <<std::endl;} T(long,double){std::cout<<__LINE__<< ":dd" <<std::endl;} T(std::initializer_list<std::string>){std::cout<<__LINE__ << ":ss" <<std::endl;} }; int main() { T a{}; T b({}); T c{{}}; return 0; }
分析
{}
無參,表示默認構造.而不是無參的std::initializer_list
.
b,c
表示無參std::initializer_list
.
模板不支持
案例
#include <iostream> template <typename T> void show( T&& a) { } int main () { show({1,2,3,4,5}); }
分析
需要兩次推導,不支持.分別是
template
推導和{}
裏面的類型推導.
總結
注意
如果構造有重載
std::initializer_list
,會對創建產生影響,需要謹慎選擇{},()
.優先級
std::initializer_list
盡力推導,可以通過窄化推導的則報錯.不往下繼續.無法推導則拆分爲構造函數.
無法推導則拆分爲成員變量初始化.
成員變量按照元素挨個初始化.