Templates and argument dependent lookup
When compiling this program, I was expecting the operator<< call to resolve to the one in the global namespace, but instead, the compiler reports an ambiguous overload. I thought non-dependent lookup occurred before the functions in namespaces that are included as potential matches due to argument dependent lookup. This seems to be the case for non-template functions.
Can someone explain?
#include <iostream>
class Foo
{};
namespace NS
{
class Stream
{};
template <typename T>
Stream& operator << ( Stream& s, T t)
{
std::cerr << "Namespace call!\开发者_运维技巧n";
return s;
}
}
template<typename STREAM>
STREAM& operator << ( STREAM& s, Foo f )
{
std::cerr << "Global NS call";
return s;
}
/**
* This function (as opposed to the one above) is not ambiguous. Why?
NS::Stream& operator << ( NS::Stream& s, Foo f )
{
std::cerr << "Global NS call";
return s;
}
*/
int main()
{
Foo f;
NS::Stream s;
s << f;
return 0;
}
Compiler Output:
test11.cpp: In function ‘int main()’:
test11.cpp:28: error: ambiguous overload for ‘operator<<’ in ‘s << f’
test11.cpp:18: note: candidates are: STREAM& operator<<(STREAM&, Foo) [with STREAM = NS::Stream]
test11.cpp:13: note: NS::Stream& NS::operator<<(NS::Stream&, T) [with T = Foo]
There are two possible candidates for s << f
: the global one and the namespace'd one. There is nothing to choose between those two for the C++ compiler, so it is ambiguous.
Even though this is an old question, I think there are still some things that haven't been clarified regarding the OP's specific questions, so here we go.
First of all: argument-dependent lookup and normal unqualified lookup are both done for unqualified function and operator calls. This applies to both normal functions and function template specializations. According to [3.4.2 paragraph 3], the only exceptions are if normal unqualified lookup finds:
- a declaration of a class member, or
- a block-scope function declaration that is not a using-declaration, or
- a declaration that is neither a function or a function template.
Only in the cases above, argument-dependent lookup is not performed. As you can see, none of these apply in this case.
So, both declarations are found. Now, overload resolution needs to be performed to choose the best viable function. The arguments fit the parameter types perfectly in both cases, so one overload cannot be chosen over another based on better conversions. Both are template specializations, so, as a last resort, partial ordering of function templates is used to try to determine if one is more specialized than the other. Alas, the template in NS is more specialized for the first argument, and the global one is more specialized for the second argument, so no template is more specialized than the other. Conclusion: no overload can be chosen over the other, the call is ambiguous.
Now, for your second question, regarding the operator definition that's commented out. If you uncomment that definition, in this case, too, ADL is performed; all three overloads are found by name lookup. Again, all arguments match the parameter types perfectly. What's different is that the last definition is a normal operator function, not a template. If no overload can be chosen over the others based on conversions, then, if one is a normal function and all the others are template specializations, the non-template one is preferred over the others. That's why the call is no longer ambiguous in this case.
Standard references are to N4140, the last C++14 draft before publication, but I don't think any of the above changed since C++03.
The global namespace has no special priority. The problem with s << f
is that both arguments are associated with a namespace: s
with ::NS
and f
with ::
.
Given that the global namespace is just like any other (except that it is always in scope, which does not matter here), the two function overloads are exactly tied for best match and there is nothing the compiler can do.
When using the IOStreams library, this problem is resolved by accepting parameters of type istream &
or ostream &
, without template parameterization.
There is ambiguity because you have defined Stream
inside namespace NS
. If you define Stream
in global namespace, then there will be no ambiguity.
The compiler will try to resolve which function to choose according to the arguments to the unqualified function and their associated namespaces. Refer to Section 3.4.2 - Argument-dependent name lookup, of ISO/IEC 14882:2003 standard. As one argument is defined in global namespace and one argument is defined in NS, the compiler does not know which function to use.
精彩评论