2011-08-18 30 views
7

這個問題是參考this,這是稍早發佈的。
雖然OP很高興接受解決了他的問題的答案,但我對編譯器爲什麼會給出一個看似錯誤的錯誤有點感興趣。編譯器爲什麼錯誤地檢測不到正確的函數簽名?

下面是我創建展示一個小的代碼示例相同的:

class YourClass 
    { 
    }; 

    class YourClass2 
    { 
    }; 
    class MyClass 
    { 
     public: 
      void doSomething(YourClass2 obj) 
      { 
       //Nothing more Interesting to do 
      } 

    }; 

    int main() 
    { 
     YourClass *ptr = new YourClass(); 
     MyClass obj; 
     obj.doSomething(ptr); 

     return 0; 
    } 

編譯thisGCC(4.3.4)給出一個看似奇怪的錯誤結果:

prog.cpp: In function ‘int main()’: 
prog.cpp:23: error: no matching function for call to ‘MyClass::doSomething(YourClass*&)’ 
prog.cpp:13: note: candidates are: void MyClass::doSomething(YourClass2) 

所以問題是:
爲什麼編譯器處理呼叫,

obj.doSomething(ptr); 

呼叫與原型的功能,

MyClass::doSomething(YourClass*&) 

,而不是

MyClass::doSomething(YourClass*) 

這似乎是明顯的情況下。

回答

4

正如其他人所說,編譯器試圖有幫助,並可能使您感到困惑。讓我們從最簡單的錯誤開始:

error: no matching function for call to obj.doSomething(ptr)

儘管錯誤消息是正確的,但它提供的信息非常有限。在這個例子中,代碼很簡單,但考慮更復雜的一段代碼。你閱讀錯誤信息,你可能會想... 什麼是obj?,什麼是ptr?所以它試圖幫助你,告訴你什麼obj是:

error: no matching function for call to ‘MyClass::doSomething(ptr)’

嗯,這是更好,這是告訴你,至少在你需要找過載,但認爲該班是什麼類std::ostream和功能operator<< ...有太多的重載,而且,ptr的類型是什麼?因此,它向前移動並試圖描述參數:該參數是一個左值YourClass* ...我已經看到在過去產生的類型的錯誤消息:

error: no matching function for call to ‘MyClass::doSomething’ that takes an YourClass* lvalue as argument.

好了,所以錯誤報告已完成。現在認爲該函數可能有更多的參數,並且錯誤消息可以變成一個複雜的野獸(想象一下5個「類型XXX的右值和YYY類型的左值」的列表)。接下來的事情就是使錯誤信息的語法同樣精確(參數的性質是重要的,或者是重要的,這樣信息就必須存在)。因此,它再次重寫錯誤消息:

error: no matching function for call to ‘MyClass::doSomething(YourClass*&)’

的問題是,你正在試圖解釋,作爲一個函數簽名,但比較函數調用的說明。

錯誤消息沒有在標準中定義,它們在編譯器和編譯器之間是不同的,甚至在同一個編譯器中從一個版本到另一個版本。在一天結束時,您需要學習讀取錯誤消息及其含義。

+0

+1。我喜歡逐步分析編譯器消息。 :-)。最重要的一點是「問題在於你試圖將其解釋爲函數簽名,而不是函數調用的描述。」 – Nawaz

0

編譯器不需要生成應該對您有意義的消息。所有它需要告訴你,你的代碼中有一個錯誤。現在它打印什麼信息取決於編譯器。該標準沒有說明它應該打印什麼信息,什麼不是。當編譯器打印錯誤消息時,在我看來,該錯誤消息可能是好的/有幫助的,或者是壞的/無益的,但不是「錯誤的」。

+2

我知道,問題是,編譯器不需要特別的努力來檢測那個函數簽名,但它給出了一個不匹配的診斷消息,問題是爲什麼? –

6

首先,請注意,表達ptr(不是可變ptr)具有類型YourClass *&。這個很重要;這是參考類型可以工作的唯一方式(否則當您做YourClass *&x = ptr時,您會參考複製,這也是爲什麼YourClass *&x = (ptr + 1)失敗)。因此,編譯器開始搜索MyClass::doSomething(YourClass *&)的函數。

當然,這個調用可以匹配原型MyClass::doSomething(YourClass *)。它也可以匹配MyClass::doSomething(const YourClass *)或許多其他。可能有數十個(或者多個參數,數百或數千個)原型可能與此調用相匹配;但是,沒有人能找到。

因此,編譯器放棄,並給出一個錯誤。在錯誤中,寧願列出每個理論上可能的匹配,它引用最接近它最初尋找的原型;那就是,MyClass::doSomething(YourClass *&)

+0

第一個簽名是最普遍的簽名是什麼?我認爲這是一個隱式轉換,而不是第二個轉換。 –

+0

@bdonlan:嗯,它引發了另一個問題:編譯器爲什麼要這樣搜索?標準需要那樣嗎? – Nawaz

+1

雖然您可以將Foo轉換爲Foo&,但這意味着即使參考文件是可能的,也會強制複製。 – bdonlan

1

編譯器試圖有幫助。

MyClass::doSomething(YourClass*&) 

這不會命名任何函數:在您的代碼中沒有與此匹配的函數。

YourClass*&是你試圖傳遞到名爲MyClass::doSomething函數的參數的類型:參數(ptr)是一個左值,由&在錯誤表示。

編譯器需要一些方法來區分左值參數和右值參數,以便爲您提供儘可能多的有用診斷信息;這是一個簡單的方法來做到這一點。

相關問題