2011-07-27 36 views
2

的順序我知道C++繼承自C多的要求,其中之一是,除非它以前曾遇到過一個原型或定義這個函數,編譯器將無法識別全局函數。C++的困惑在函數重載和申報

這也影響C++函數重載。如果函數調用有多個候選者,那麼如果編譯器還沒有看到它的原型/定義,那麼「正確的」候選者將不會被包含在選擇過程中。

考慮:

void foo(int v) 
{ 
    cout << "Int version" << endl; 
} 

template <class T> 
void call_foo(const T& v) 
{ 
    foo(v); 
} 

void foo(const std::string& v) 
{ 
    cout << "Overload for string" << endl; 
} 

這裏,call_foo(std::string("abc"))通話將導致編譯器錯誤,即使是foostd::string過載。問題在於之前的函數模板call_foo被定義爲編譯器看到過載。

然而,這似乎並不適用於全球運算符重載。我們定期重載std::ostream& operator << (std::ostream& os, const T&);以使我們的自定義類型與C++ ostreams兼容。無論運算符重載函數在何處定義,編譯器都會選擇正確的重載。

例如:

struct Bar { }; 

template <class T> 
void dispatch(const T& v) 
{ 
    std::cout << v << std::endl; 
} 

std::ostream& operator << (std::ostream& os, const Bar& b) 
{ 
    os << "Outputting Bar..."; 
    return os; 
} 

在這裏,如果我們調用dispatch(Bar()),編譯器調用正確的過載,輸出Outputting Bar...

所以,看來C++標準允許更高級的行爲,當談到選擇運算符重載候選函數。

我的問題是,爲什麼這種能力不能擴展到常規函數重載?我意識到需要與C向後兼容,但這不會對此產生任何影響,因爲只要您編寫函數重載,您就不會編寫C程序。

+0

第二個'foo'不是專門化的,它是另一個重載名稱foo的函數。 – pmr

+0

正確,已更正。 – Channel72

+0

我不明白你的'operator <<'例子,因爲它與你的第一個例子沒有任何比較。沒有調度模板,只是各種重載。 – GManNickG

回答

5

首先,有沒有必要編譯器看到的定義,當它 實踐重載決議;一個聲明就夠了。其次, 問題比您似乎意識到的要複雜得多。在你的函數模板 call_foofoo是一個從屬名稱,因爲它是在 方面取決於實例化類型使用。這意味着它將被查找兩次,一次是在定義模板 的地方,第二次是模板實例化的地方。然而,這個第二次查詢是純粹的ADL;它不會找到由ADL引入的不是 的聲明。在你的情況,你想要的foo在全球 命名空間,但std::string調用它時,由ADL認爲是唯一的命名空間 是std::

在第二個示例中,Bar位於全局名稱空間中,因此將考慮在實例化位置的全局名稱空間中的聲明。 (請注意,如果dispatch不是模板,則不會考慮 。)