曾經(jīng)無(wú)數(shù)次被這個(gè)問(wèn)題困擾:
class A {};
A a();
代碼編譯不過(guò)娘赴。會(huì)提示找不到函數(shù)a的定義....
如果一直這樣也就罷了规哲,可有些時(shí)候,你這樣寫(xiě):
class A
{
public:
A(int x) {}
} ;
A a(1);
代碼居然又可以編譯過(guò)去了诽表。
這樣的問(wèn)題在形式上的不統(tǒng)一往往讓人有種心里上的別扭感覺(jué)唉锌,總覺(jué)得哪里不對(duì)「妥啵可這個(gè)問(wèn)題又太微小袄简,對(duì)程序的功能也好性能也罷,沒(méi)有任何影響泛啸,所以每次遇到這樣的問(wèn)題绿语,稍一修改,也就放過(guò)了候址。后來(lái)慢慢習(xí)慣了吕粹,覺(jué)得是c++語(yǔ)法的不嚴(yán)謹(jǐn)造成的。前一陣翻《effective stl》岗仑,才發(fā)現(xiàn)這個(gè)問(wèn)題的真正原因匹耕。
問(wèn)題的引出是這樣的一段代碼:
ifstream dataFile("ints.data");
list<int> data(istream_iterator<int>(dataFile), istream_iterator<int>());
這段代碼的本意是構(gòu)造一個(gè)list<int>對(duì)象,它可以通過(guò)編譯荠雕,但實(shí)際上并沒(méi)有按照你設(shè)想的來(lái)泌神。
先從最基本的說(shuō)起。
下面的代碼聲明了一個(gè)帶double參數(shù)并返回int的函數(shù):
int f(double d);
下面的代碼做了同樣的事情舞虱。參數(shù)d兩邊的括號(hào)是多余的,會(huì)被忽略:
int f(double (d));
下面的代碼聲明了同樣的函數(shù)母市。只是它省略了參數(shù)名稱(chēng):
int f(double);
現(xiàn)在再看三個(gè)函數(shù)的聲明矾兜。第一個(gè)聲明了一個(gè)函數(shù)g,它的參數(shù)是一個(gè)指向不帶任何參數(shù)的函數(shù)的指針患久,該函數(shù)返回double值:
int g(double (*pf)()); //g以指向函數(shù)的指針為參數(shù)
有另外一種方式可表名同樣的意思椅寺。唯一的區(qū)別是,pf用非指針的形式來(lái)聲明(這種形式在c和c++中都有效):
int g(double pf()); //pf 為隱式指針
跟上面的例子一樣蒋失,參數(shù)名key省略返帕,因此下面是g的第三種聲明,其中參數(shù)名pf被省略了:
int g(double ()); //省略參數(shù)名
請(qǐng)注意圍繞參數(shù)名的括號(hào)(比如對(duì)f的第二個(gè)聲明中的d)與獨(dú)立的括號(hào)的區(qū)別篙挽。圍繞參數(shù)名的括號(hào)被忽略荆萤,而獨(dú)立的括號(hào)則表名參數(shù)列表的存在,他們說(shuō)明存在一個(gè)函數(shù)的指針參數(shù)。
回到開(kāi)始的問(wèn)題链韭,下面這段代碼:
list<int> data(istream_iterator<int>(dataFile), istream_iterator<int>());
實(shí)際上聲明了一個(gè)函數(shù)data偏竟,其返回值是list<int>,它有兩個(gè)參數(shù):
1 dataFile敞峭,它的類(lèi)型是istream_iterator<int>踊谋。dataFile兩邊的括號(hào)是多余的,會(huì)被忽略旋讹。
2 第二個(gè)參數(shù)沒(méi)有名稱(chēng)殖蚕,它的類(lèi)型是指向不帶參數(shù)的函數(shù)的指針,該函數(shù)返回一個(gè)istream_iterator<int>沉迹。
之所以這樣睦疫,源于c++里的一條普遍規(guī)律:盡可能地將語(yǔ)句解釋為函數(shù)聲明。
因此呢胚股,本文開(kāi)頭的 A a()笼痛,也被解釋為一個(gè)名為a的函數(shù),不帶任何參數(shù)琅拌,返回一個(gè)A對(duì)象缨伊。
當(dāng)知道了這樣的機(jī)制之后,對(duì)一些詭異的問(wèn)題終于可以找到一個(gè)合理的解釋了进宝。
于無(wú)聲處聽(tīng)驚雷刻坊,于細(xì)微處見(jiàn)真知。