C++11系列-改進(jìn)的類型推導(dǎo):auto、decltype和新的函數(shù)語法
C++11系列-改進(jìn)的類型推導(dǎo):auto、decltype和新的函數(shù)語法
C++11引入了一些新的實(shí)用的類型推導(dǎo)能力,這意味著你可以花費(fèi)更少的時間去寫那些編譯器已經(jīng)知道的東西。當(dāng)然有些時候你需要幫助編譯器或者你的編程伙伴。但是C++11,你可以在一些乏味的東西上花更少的時間,而多去關(guān)注邏輯本身。
auto之樂
我們先快速回顧一下auto,萬一你沒有讀第一篇C++11文章中關(guān)于auto的部分。在C++11中,如果編譯器在定義一個變量的時候可以推斷出變量的類型,不用寫變量的類型,你只需寫auto即可。
int?x?=?4;
現(xiàn)在可以這樣寫:
auto?x?=?4;
這當(dāng)然不是auto預(yù)期的用途!它會在模板和迭代器的配合使用中閃耀光芒:
vectorvec; auto?itr?=?vec.iterator();
其它時候auto也會非常有用。比如,你有一些下面格式的代碼:
templatevoid makeAndProcessObject?(const?Builder&?builder) { ????BuiltType?val?=?builder.makeObject(); ????//?do?stuff?with?val }
上面的代碼,我們看到這里需要兩個模板參數(shù):一個是Builder對象的類型,另一個是Builder創(chuàng)建出的對象的類型。糟糕的是創(chuàng)建出的類型無法被推導(dǎo)出,所以每次你必須這樣調(diào)用:
MyObjBuilder?builder; makeAndProcessObject(?builder?);
但是auto立即將丑陋的代碼一掃無余,當(dāng)Builder創(chuàng)建對象時不用寫特殊代碼了,你可以讓C++幫你做:
templatevoid makeAndProcessObject?(const?Builder&?builder) { ????auto?val?=?builder.makeObject(); ????//?do?stuff?with?val }
現(xiàn)在你僅需一個模板參數(shù),而且這個參數(shù)可以在函數(shù)調(diào)用的時候輕松推導(dǎo):
MyObjBuilder?builder; makeAndProcessObject(?builder?);
這樣更易調(diào)用了,并且沒丟失可讀性,卻更清晰了。
decltype和新的返回值語法
現(xiàn)在你可能會說auto就這樣嗎,假如我想返回Builder創(chuàng)建的對象怎么辦?我還是需要提供一個模板參數(shù)作為返回值的類型。好!這充分證明了標(biāo)準(zhǔn)委員有一群聰明的家伙,對這個問題他們早想好了一個完美的解決方案。這個方案由兩部分組成:decltype和新的返回值語法。
新的返回值語法
讓我們講一下新的返回值語法,這個語法還能看到auto的另一個用處。在以前版本的C和C++中,返回值的類型必須寫在函數(shù)的前面:
int?multiply(int?x,?int?y);
在C++11中,你可以把返回類型放在函數(shù)聲明的后面,用auto代替前面的返回類型,像這樣:
auto?multiply(int?x,?int?y)?->?int;
但是為什么我要這樣用?讓我們看一個證明這個語法好處的例子。一個包含枚舉的類:
class?Person { public: ????enum?PersonType?{?ADULT,?CHILD,?SENIOR?}; ????void?setPersonType?(PersonType?person_type); ????PersonType?getPersonType?(); private: ????PersonType?_person_type; };
我們寫了一個簡單的類,里面有一個類型PersonType表明Person是小孩、成人和老人。不做特殊考慮,我們定義這些成員方法時會發(fā)生什么? 第一個設(shè)置方法,很簡單,你可以使用枚舉類型PersonType而不會有錯誤:
void?Person::setPersonType?(PersonType?person_type) { ????_person_type?=?person_type; }
而第二個方法卻是一團(tuán)糟。簡單的代碼卻編譯不過:
//?編譯器不知道PersonType是什么,因?yàn)镻ersonType會在Person類之外使用 PersonType?Person::getPersonType?() { ????return?_person_type; }
你必須要這樣寫,才能使返回值正常工作
Person::PersonType?Person::getPersonType?() { ????return?_person_type; }
這可能不算大問題,不過會容易出錯,尤其是牽連進(jìn)模板的時候。
這就是新的返回值語法引進(jìn)的原因。因?yàn)楹瘮?shù)的返回值出現(xiàn)在函數(shù)的最后,而不是前面,你不需要補(bǔ)全類作用域。當(dāng)編譯器解析到返回值的時候,它已經(jīng)知道返回值屬于Person類,所以它也知道PersonType是什么。
auto?Person::getPersonType?()?->?PersonType { ????return?_person_type; }
好,這確實(shí)不錯,但它真的能幫助我們什么嗎?我們還不能使用新的返回值語法去解決我們之前的問題,我們能嗎?不能,讓我們介紹新的概念:decltype。
decltype
decltype是auto的反面兄弟。auto讓你聲明了一個指定類型的變量,decltype讓你從一個變量(或表達(dá)式)中得到類型。我說的是什么?
int?x?=?3; decltype(x)?y?=?x;?//?相當(dāng)于?auto?y?=?x;
可以對基本上任何類型使用decltype,包括函數(shù)的返回值。嗯,聽起來像個熟悉的問題,假如我們這樣寫:
decltype(?builder.makeObject()?)
我們將得到makeObject的返回值類型,這能讓我們指定makeAndProcessObject的返回類型。我們可以整合進(jìn)新的返回值語法:
templateauto makeAndProcessObject?(const?Builder&?builder)?->?decltype(?builder.makeObject()?) { ????auto?val?=?builder.makeObject(); ????//?do?stuff?with?val ????return?val; }
這僅適用于新的返回值語法,因?yàn)榕f的語法下,我們在聲明函數(shù)返回值的時候無法引用函數(shù)參數(shù),而新語法,所有的參數(shù)都是可訪問的。
auto:引用、指針和常量
下面要確定的一個問題是auto如何處理引用:
int&?foo(); auto?bar?=?foo();?//?int&?or?int?
答案是在C++11中,auto處理引用時默認(rèn)是值類型,所以下面的代碼bar是int。不過你可以指定&作為修飾符強(qiáng)制它作為引用:
int&?foo(); auto?bar?=?foo();?//?int auto&?baz?=?foo();?//?int&
不過,假如你有一個指針auto則自動獲取指針類型:
int*?foo(); auto?p_bar?=?foo();?//?int*
但是你也可以顯式指定表明變量是一個指針:
int*?foo(); auto?*p_baz?=?foo();?//?int*
當(dāng)處理引用時,你一樣可以標(biāo)記const,如果需要的話:
int&?foo(); const?auto&?baz?=?foo();?//?const?int&
或者指針:
int*?foo(); const?int*?const_foo(); const?auto*?p_bar?=?foo();?//?const?int* auto?p_bar?=?const_foo();?//?const?int*
所有這些都很自然,并且這遵循C++模板中類型推導(dǎo)的規(guī)則。