2012-05-30 64 views
1

我試圖運行從書「加速C++」(A.凱尼格,B.武)一些示例代碼(§8.2.2):爲什麼編譯器在這裏抱怨函數歧義?

#include <iostream> 
#include <vector> 

using std::cout; 
using std::endl; 
using std::vector; 

template <class In, class X> 
In find(In begin, In end, const X &x) 
{ 
    while (begin != end && *begin != x) { 
     ++begin; 
    } 

    return begin; 
} 

int main() 
{ 
    vector<int> v; 

    v.push_back(5); 
    v.push_back(32); 
    v.push_back(42); 
    v.push_back(7); 

    cout << *find(v.begin(), v.end(), 42) << endl; 

    return 0; 
} 

find功能看起來像這樣的書;我自己寫的main函數。

clang ++和g ++都不會編譯它。看起來好像他們抱怨我的find函數引入了std::find的歧義。但是,我從來沒有在代碼中使用using namespace::std;using std::find;,所以如果包含它,編譯器甚至不應該被允許使用std::find。這裏發生了什麼?

+1

[Works for me](http://ideone.com/0SZQ7) –

+0

你使用的是什麼版本的clang ++和g ++?你能給我們你收到的錯誤信息嗎?你使用什麼編譯器選項?我用g ++ - 4.6.2和g ++ - 4.7.0測試過,沒有任何問題。 –

+0

注意如果我們添加'#include ',g ++ 4.6.3會發出錯誤。 – aschepler

回答

4

我想你已經絆倒了「Koenig lookup」(是的,同樣的Koenig,所以你會認爲他會發現問題),又名「ADL」。

假設一下,如果通過間接包括<algorithm>已經拉。

如果std::vector<int>::iterator(參數的類型)是在命名空間std一類,然後std::find是您的來電比賽,即使你永遠不會「使用」它,所以這個呼叫是不明確的。

如果std::vector<int>::iteratorint*,那麼std::find不是候選者,並且該調用不是不明確的。

無論哪種方式是std::vector有效的實施,這也是實現定義要麼<iostream><vector>是否包含<algorithm>。所以你的代碼是不可移植的,但是實現幾乎不能診斷可移植性問題,除非代碼實際上在該實現上失敗。

在ADL的更多典型案例中,相關名稱空間中的函數或函數模板是比調用方所居住或「使用」的名稱空間中的函數或函數模板更好的候選項(更具體),因此可避免歧義。但是,您的全球findstd::find基本相同,因此不適用。

http://ideone.com/Cskur

#include <iostream> 

namespace foo { 
    struct Foo {}; 

    /* same as the one we'll define later */ 
    template <typename T> 
    void func(T) { 
     std::cout << "namespace function template\n"; 
    } 
    /* this would be a better match 
    void func(Foo) { 
     std::cout << "namespace function\n"; 
    } 
    */ 
} 

template <typename T> 
void func(T) { 
    std::cout << "global function template\n"; 
} 

int main() { 
    foo::Foo f; 
    func(f); 
} 

結果:

prog.cpp:19: error: call of overloaded ‘func(foo::Foo&)’ is ambiguous 

結論:它是有點危險,從std使用的名稱,甚至在其他的命名空間。

或者如果您願意:在其他命名空間中定義來自std的名稱可能意味着呼叫者需要小心。差異主要在於當錯誤發生時誰修復了這個錯誤。

+2

我認爲一個更好的結論是,使用'std'中的名字而不限制它們是有點危險的。例如':: find(/ * etc。* /)'會在這裏工作。 –

+0

Nitpicky:'std :: vector :: iterator'可以是來自不同命名空間的類,並且由於模板和繼承規則,仍然具有'std'作爲關聯的命名空間。例如,我的類型爲'__gnu_cxx :: __ normal_iterator >'的實現typedefs。 – aschepler

+2

@詹姆斯:當然會有效。我的意思是,在另一個命名空間中定義一個來自'std'的名字會有些危險,因爲有人可能稱它爲不合格。如果所有調用都必須是':: find',那麼我不妨調用函數'x_find'而不是'find'。當然,有一個問題是你不知道未來的標準將會添加到名稱空間'std'中,因此要小心其邏輯結論:不要使用可能在不受控制的命名空間中的參數進行非限定調用,除非你想要ADL。 –

相關問題