tajmahal
tajmahal

Reputation: 1675

Why does the compiler complain about function ambiguity here?

I am trying to run some sample code from the book "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;
}

The find function appears like this in the book; the main function I wrote myself.

Both clang++ and g++ won't compile this. It seems as if they are complaining that my find function introduced an ambiguity with std::find. However, I never used using namespace::std; nor using std::find; in the code, so the compiler shouldn't even be allowed to use std::find if it was included. What's going on here?

Upvotes: 1

Views: 302

Answers (1)

Steve Jessop
Steve Jessop

Reputation: 279225

I think you've tripped over "Koenig lookup" (yes, the same Koenig, so you'd think he'd spot the problem), aka "ADL".

Suppose for a moment that via an indirect include, <algorithm> has been pulled in.

If std::vector<int>::iterator (the type of the argument) is a class in namespace std, then std::find is a match for your call even though you never "used" it, so the call is ambiguous.

If std::vector<int>::iterator is int*, then std::find is not a candidate and the call is not ambiguous.

Either way is a valid implementation of std::vector, and it's also implementation-defined whether or not either <iostream> or <vector> includes <algorithm>. So your code isn't portable, but implementations are pretty much incapable of diagnosing the portability issue unless the code actually fails on that implementation.

In more typical cases of ADL, the function or function template in the associated namespace is a better candidate (more specific) than the one in the namespace that the caller inhabits or "uses", so ambiguity is avoided. But your global find is basically identical to std::find, so that doesn't apply.

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);
}

Result:

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

Conclusion: it is somewhat dangerous to use names from std, even in other namespaces.

Or if you prefer: defining names from std in other namespaces may mean that callers need to be careful. The difference is mostly a matter of who fixes the bug when it happens.

Upvotes: 4

Related Questions