为什么编译器会在这里抱怨函数歧义?

时间:2012-05-30 23:57:03

标签: c++ stl

我试图从“Accelerated C ++”一书中运行一些示例代码(A. Koenig,B。Moo)(§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}}。这是怎么回事?

1 个答案:

答案 0 :(得分:4)

我认为你已经超过了“Koenig查询”(是的,同样的Koenig,所以你认为他已经发现了问题),又名“ADL”。

假设暂时通过间接包含,<algorithm>已被拉入。

如果std::vector<int>::iterator(参数的类型)是命名空间std中的一个类,那么std::find就是您的通话匹配,即使您从未“使用过”它,所以电话是模棱两可的。

如果std::vector<int>::iteratorint*,则std::find不是候选人,并且通话不明确。

无论哪种方式都是std::vector的有效实施,而且无论<iostream>还是<vector>是否包含<algorithm>,它都是实施定义的。所以你的代码不可移植,但实现几乎无法诊断可移植性问题,除非代码实际上失败了。

在更典型的ADL案例中,关联命名空间中的函数或函数模板比​​调用者所居住或“使用”的命名空间中的函数或函数模板更好(更具体),因此避免了歧义。但是,您的全局find基本上与std::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的名称可能意味着呼叫者需要小心。差异主要在于谁在错误发生时修复了错误。