Reputation: 1675
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
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.
#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