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