天天看點

C++之auto、decltype、typeid、initializer_list、explicit關鍵字1.auto關鍵字2.decltype關鍵字3.typeid關鍵字4.initializer_list關鍵字5.explicit關鍵字

文章目錄

  • 1.auto關鍵字
    • 1.1 auto關鍵字作用
    • 1.2 auto關鍵字與頂層const和底層const
    • 1.3 auto關鍵字與指針和引用
    • 1.4 auto關鍵字與數組和函數
    • 1.5 auto關鍵字與使用new動态配置設定
    • 1.6 auto關鍵字與使用的場景
  • 2.decltype關鍵字
    • 2.1 decltype關鍵字作用
    • 2.2 decltype關鍵字與頂層const和底層const
    • 2.3 decltype關鍵字與普通變量
    • 2.4 decltype關鍵字與指針和引用
    • 2.5 decltype關鍵字總結
    • 2.6 decltype關鍵字與decltype(auto)
    • 2.7 decltype關鍵字與auto關鍵字差別
  • 3.typeid關鍵字
    • 3.1 typeid關鍵字作用
    • 3.2 typeid關鍵字類型識别和函數
    • 3.3 typeid關鍵字與類對象
  • 4.initializer_list關鍵字
    • 4.1 initializer_list關鍵字作用
    • 4.2 initializer_list關鍵字與vector差別
    • 4.3 initializer_list關鍵字提供的操作
  • 5.explicit關鍵字

1.auto關鍵字

1.1 auto關鍵字作用

  • 在C++11新标準中引進了auto類型說明符,使用它能夠讓編譯器代替我們去分析表達式所屬的類型。auto 自動類型推斷發生在編譯期,是以使用 auto 關鍵字不會降低程式的運作效率。

1.2 auto關鍵字與頂層const和底層const

  • 如果定義的對象不是指針或者引用,則const屬性會被丢棄。例如:
int i=2;
const int ci=i,&cr=ci;
auto a=ci; //a是整型(ci的頂層const被忽略);
auto b=cr; //b是整型(cr的引用被忽略);
           
  • auto如果我們希望推出的auto類型是頂層const,我們需要明确指出。例如:

    const int ci=2;

    const auto a=ci;

1.3 auto關鍵字與指針和引用

  • 如果定義的對象是指針或者引用,則const屬性被保留。
  • auto關鍵字與指針:設定類型為auto的指針,初始值的頂層const屬性仍然保留。
int i=2;
const int ci=i;
auto a=&i;  //a是整型指針(整數的位址就是指向整數的指針);
auto b=&ci; //b是指向整型常量的指針(對const對象取位址是一種底層const);

//用auto聲明指針類型時,用auto和auto *沒有任何差別;
auto *pi=&i;
auto pi=&i;  //兩種方式沒有差别;
//用auto聲明引用類型時則必須加&;
auto &ri=i; //ri為引用;
auto ri=i;  //ri為int;
           
  • auto關鍵字與引用:在使用auto關鍵字,使用引用其實是使用引用的對象。特别是當引用被用作初始值時,真正參與初始化的其實是引用的對象的值,此時編譯器以引用對象的類型作為auto的類型。另外設定類型為auto的引用,初始值的頂層const屬性仍然保留。例如:
int x = 0, &rx = x; 
auto a1 = rx;  //使用引用其實是使用引用的對象,
			   //此時auto以引用對象的類型作為auto的類型,
			   //是以auto這裡被推斷為 int;
auto &a2 = rx; //此時auto被推斷為int類型,a2本身就是int &類型;
const auto &a3 = rx;//auto被推斷為int類型,a3對象本身是const int &類型;
					//不能通過a3去修改rx引用的對象值;

int i=2; const int ci=i;
auto &a=ci; //此時auto被推斷為const int類型,a本身就是const int &類型;
auto &b=42; //錯誤,不能為非const引用綁定字面值;
const auto &c=43; //正确,可以為const引用綁定字面值;
           

1.4 auto關鍵字與數組和函數

  • 在一些情況下,數組的操作實際上是指針的操作。意味着适用數組作為一個auto變量的初始值時,推斷得到的類型是指針而非數組。例如:
int ia[]={1,2,3,4,5};
auto ia2(ia); //此時ia2是整型指針,指向ia的第一個元素,
			  //相當于auto ia2(&ia[0]);
decltype(ia) ia3={0,1,2,3,4}; //此時ia3是一個數組;


const char arr[] = "I Love China";
auto r1 = arr; //如果将數組名指派給auto變量,那麼auto推斷的結果是指針類型,
			   //如果有const屬性保留,
			   //auto推斷的結果是const char *;
auto &r2 = arr;// 如果将數組名指派給auto &變量,
            //auto &變量的類型是一個數組引用類型,即為const char (&)[14]; 


int add(int a,int b);  //函數聲明;
auto r3 = add;         //r3為int(*)(int, int);
auto &r4 = add;        //r4為int(&)(int, int);
           

1.5 auto關鍵字與使用new動态配置設定

  • 如果使用括号包圍的初始化器,可以使用auto從初始化器中推斷想要配置設定對象的類型。隻有當括号僅有單一初始化器才能使用auto關鍵字。例如:

    auto p1 = new auto (obj); //p1指向一個與obj類型相同的對象,該對象用obj進行初始化;

    auto p1 = new auto{a,b,c}; //錯誤,括号内隻能有單個初始化器;

    p1的類型是指針類型,指向從obj自動推斷出的類型;若obj是int,那麼p1就是int*。新配置設定的對象用obj的值進行初始化。

1.6 auto關鍵字與使用的場景

  • 适用的場景:
  • 一些類型長度書寫很長的,可以用 auto 來簡化。例如

    for(std::vector<int>::iterator it = v.begin();it != v.end();++it)

    ,如果使用auto可以直接寫為

    for(auto it = v.begin();it != v.end();++it)

  • 當函數傳回的值不确定時,可以用auto作為傳回值類型,更加友善。編譯器會根據傳回值的類型推斷 auto 的類型,這種文法是在 C++14 才出現的。例如:

    auto func() { return 0; }

  • 不适用的場景:
  • 函數形參不能是auto類型,比如

    int add(auto a, auto b) { return a + b; }

    是不允許的。
  • 類的非static成員變量不可以是auto類型。類的static成員變量可以是auto類型的,但需要用const修飾,而且該變量需要類内初始化。例如:

    auto static const i=4;

  • auto 不能直接用來聲明數組。
  • 執行個體化模闆時不能使用auto作為模闆參數。

2.decltype關鍵字

2.1 decltype關鍵字作用

  • C++11新标準引進decltype關鍵字,作用是選擇并傳回操作數的資料類型,在此過程中編譯器分析表達式并得到它的類型,卻不實際計算表達式的值。 例如

    decltype(fun()) sum=x;

    ,編譯器實際上沒有調用fun函數,而是使用fun()的傳回值類型作為sum的資料類型。decltype關鍵字也是用來在編譯時推導出一個表達式的類型,但此表達式初始化與否在編譯器都沒有多大的影響。

2.2 decltype關鍵字與頂層const和底層const

  • decltype處理頂層const和引用的方式與auto有些不同,如果decltype使用的表達式是個變量,則decltype傳回該變量的類型(包括頂層const和引用在内)。例如:
const int ci=0,&cj=ci;
decltype(ci) x=0; //x的類型是const int;
decltype(cj) y=x; //y的類型是const int &,y綁定到變量x;
decltype(cj) z;   //錯誤,z是引用,必須初始化;
//需要指出的是,引用從來都作為其所指對象的同義詞出現,
//隻有用在decltype處是例外情況;
           

2.3 decltype關鍵字與普通變量

  • 對于非引用(指針),decltype關鍵字和auto關鍵字的作用類似,但是保留表達式的引用及const限定符,decltype能夠精确地推導出表達式定義本身的類型,不會像auto那樣在某些情況下舍棄掉引用和cv限定符。

2.4 decltype關鍵字與指針和引用

  • 使用關鍵字decltype的時候,左值和右值有所不同。如果表達式的求值結果是左值,decltype作用于該表達式(不是變量)得到引用。例如,假定p的類型是

    int*

    ,因為解引用符生成左值,是以

    decltype(*p)

    的結果是

    int&

    ;如果表達式的求值結果是右值,得到指向指針的指針,例如,

    decltype(&p)

    的結果是

    int**

    ,因為取位址運算符生成右值。
  • decltype與指針:decltype處理指針時需要保留指針,這點和auto是有差別。例如:
int tempA = 2;
int *ptrTempA = &tempA;
//1.正常使用dclTempA為一個int *的指針;
decltype(ptrTempA) dclTempA;
//2.需要特别注意,表達式内容為解引用操作,
//dclTempB為一個引用,引用必須初始化,故編譯不過;
decltype(*ptrTempA) dclTempB;
           
  • decltype與引用:decltype處理引用時需要保留引用,這點和auto是有差別。例如:
//非const引用
int tempA = 0, &refTempA = tempA;
//1.dclTempA為引用,綁定到tempA;
decltype(refTempA) dclTempA = tempA;
//2.雙層括号表示引用,dclTempB為引用,綁定到tempA;
decltype((tempA)) dclTempB = tempA;

//const引用;
const int ctempA = 1, &crefTempA = ctempA;
//1.dclTempE為const用,可以綁定到非const變量;
decltype(crefTempA) dclTempE = tempA;
//2.dclTempF為const引用,可以綁定到const;
decltype(crefTempA) dclTempF = ctempA;
//3.dclTempG為const引用,綁定到一個字面值;
decltype(crefTempA) dclTempG = 0;
           

2.5 decltype關鍵字總結

  • 下列給出decltype的各種用法:
//普通類型
decltype(func()) sum = 5;  // sum的類型是函數func()的傳回值的類型int, 
						   //但是這時不會實際調用函數func();
int a = 0;
decltype(a) b = 4;         // a的類型是int, 是以b的類型也是int;

//不論是頂層const還是底層const, decltype都會保留;   
const int c = 3;
decltype(c) d = c;         // d的類型和c是一樣的, 都是頂層const;
int e = 4;
const int* f = &e;         // f是底層const;
decltype(f) g = f;         // g也是底層const;


//引用與指針類型
//1. 如果表達式是引用類型, 那麼decltype的類型也是引用;
const int i = 3, &j = i;
decltype(j) k = 5; // k的類型是 const int&;

//2. 如果表達式是引用類型, 但是想要得到這個引用所指向的類型, 需要修改表達式:
int i = 3, &r = i;
decltype(r + 0) t = 5; // 此時是int類型;

//3. 對指針的解引用操作傳回的是引用類型;
int i = 3, j = 6, *p = &i;
decltype(*p) c = j; // c是int&類型, c和j綁定在一起;

//4. 如果一個表達式的類型不是引用, 但是我們需要推斷出引用, 
//那麼可以加上一對括号, 就變成了引用類型;
int i = 3;
decltype((i)) j = i; // 此時j的類型是int&類型, j和i綁定在了一起;

//解釋:如果decltype使用的是一個不加括号的變量,則得到結果就是該變量的類型;
//如果給變量加了一層或多層括号,編譯器就會當成一個表達式;
//decltype變量可以是指派語句左值的特殊表達式,這樣的decltype得到是引用類型;
//也就是decltype((var))的結果永遠是引用;
           

2.6 decltype關鍵字與decltype(auto)

  • C++14新增的類型訓示符,可以用來聲明變量以及訓示函數傳回類型。在使用時,會将“=”号左邊的表達式替換掉auto,再根據decltype的文法規則來确定類型。
int e = 4;
const int* f = &e;    //f是底層const;
decltype(auto) j = f; //j的類型是const int* 并且指向的是e;
           

2.7 decltype關鍵字與auto關鍵字差別

  • decltype和auto都可以用來推斷類型,但是二者有幾處明顯的差異。
  • auto忽略頂層const,decltype保留頂層const;
  • 對引用操作,auto推斷出原有類型,decltype推斷出引用;
  • 對解引用操作,auto推斷出原有類型,decltype推斷出引用;
  • auto推斷時會實際執行,decltype不會執行,隻做分析。總之在使用中過程中和const、引用和指針結合時需要特别小心。

3.typeid關鍵字

3.1 typeid關鍵字作用

  • typeid關鍵字的作用就是擷取一個表達式的類型。表達式可以是類型名稱、變量名、數字、字元串、指針、結構體等等,傳回結果是标準庫類型

    type_info

    對象的引用。在C++中,為了支援RTTI(運作時類型識别)提供了兩個操作符:

    dynamic_cast

    typeid

3.2 typeid關鍵字類型識别和函數

  • 如果表達式的類型是類類型且至少包含有一個虛函數,則typeid操作符傳回表達式的動态類型,需要在運作時計算;否則typeid操作符傳回表達式的靜态類型,在編譯時就可以計算。
  • t1==t2

    如果兩個對象t1和t2類型相同,則傳回true;否則傳回false。

    t1!=t2

    如果兩個對象t1和t2類型不同,則傳回true;否則傳回false。

    t.name()

    傳回類型的C-style字元串,類型名字用系統相關的方法産生。

    t1.before(t2)

    傳回指出t1是否出現在t2之前的bool值。
  • type_info

    類提供了public虛析構函數,以使使用者能夠用其作為基類。它的預設構造函數和拷貝構造函數及指派操作符都定義為private,是以不能定義或複制type_info類型的對象,程式中建立type_info對象的唯一方法是使用typeid操作符。

3.3 typeid關鍵字與類對象

  • 不帶虛函數的基類:
class base{
	public:
	    void m(){cout<<"base"<<endl;}
};
class derived : public base{
	public:
    	void m(){cout<<"derived"<<endl;}
};
           

假設定義如下指針:

base * p = new derived;

,下表将給出使用typeid操作符的結果。

typeid(p) == typeid(base*);	      //true
typeid(p) == typeid(derived*);	 //false
typeid(*p) == typeid(base);	     //true
typeid(*p) == typeid(derived);	 //false


//對于表達式typeid(p),因為p是base*類型的指針,
//是以typeid(p)==typeid(base*)為真,
//而typeid(p)==typeid(derived*)為假;

//而對于表達式typeid(*p),由于此時的基類不具有多态性;
//因而*p将會采用編譯期類型來計算,編譯期*p是base對象,
//是以表達式typeid(*p) == typeid(derived)為假,
//typeid(*p) == typeid(base)為真;
           
  • 帶虛函數的基類:
class base{
	public:
	    virtual void m(){cout<<"base"<<endl;}
};
class derived : public base{
	public:
    	void m(){cout<<"derived"<<endl;}
};
           

假設定義指針如下:

base * p = new derived;

下面給出使用typeid操作符的結果。

typeid(p) == typeid(base*);	     //true
typeid(p) == typeid(derived*);	 //false
typeid(*p) == typeid(base);	     //false
typeid(*p) == typeid(derived);	 //true

//對于表達式typeid(p),因為p是base*類型的指針,
//是以typeid(p) == typeid(base*)為真,
//而typeid(p) == typeid(derived*)為假;

//而對于表達式typeid(*p),因為base類具有多态性,
//因而在計算typeid(*p)時會根據運作時p所指向的實際類型去計算,
//而本例中p指向的是派生類對象,
//是以表達式typeid(*p) == typeid(derived)為真,
//typeid(*p) == typeid(base)為假;
           

4.initializer_list關鍵字

4.1 initializer_list關鍵字作用

  • C++11提供了initailzer_list的關鍵字,如果所有的實參類型相同,可以傳遞一個名為initailzer_list的标準庫類型。

4.2 initializer_list關鍵字與vector差別

  • initialzer_list對象中的元素永遠是const,我們無法改變initialzer_list對象中元素的值。

4.3 initializer_list關鍵字提供的操作

  • initialzer_list<T> lst;//預設初始化,T類型元素的空清單;

  • initialzer_list<T> lst{a,b,c....};//lst的元素數量和初始化一樣多,lst的元素是對應初始值的副本,清單中的元素是const;

  • lst2(lst);//或者lst2=lst;拷貝或者指派一個initialzer_list對象不會拷貝清單中的元素;拷貝後,原始清單和副本共享元素;

  • lst.size();//清單中元素的數量;

  • lst.begin();//傳回指向lst中首元素的指針;

  • lst.end();//傳回指向lst中尾元素下一位置的指針

5.explicit關鍵字

  • 類與對象這篇文章有相關介紹。