Reputation: 4339
Why doesn't the following code compile?
#include <iostream>
namespace X
{
inline std::wostream & operator<<(std::wostream & stm, int a)
{
stm << L"int";
return stm;
}
}
namespace Y
{
class A
{
};
}
inline std::wostream & operator<<(std::wostream & stream, const Y::A & msg)
{
stream << L"A";
return stream;
}
namespace X
{
void f()
{
Y::A a;
std::wcout << a;
}
}
and why removing operator <<
in namespace X, makes the code compile? Try to comment it out, for example:
namespace X
{
//inline std::wostream & operator<<(std::wostream & stm, int a)
//{
// stm << L"int";
// return stm;
//}
}
what is the dependency between these operators?
see live example.
EDIT1:
The only guess I have is that the operator declared in the same namespace where it is used hides the operators from other namespaces somehow, but I never heard about that before...
EDIT2:
Actually in my project the second operator is in namespace Z (but not global):
...
namespace Z
{
inline std::wostream & operator << (std::wostream & stream, const Y::A & msg)
{
stream << L"A";
return stream;
}
}
namespace X
{
void f()
{
using namespace Z;
Y::A a;
std::wcout << a;
}
}
that results in the same compiler error.
Upvotes: 1
Views: 102
Reputation: 141638
NOTE: lookup of overloaded operators is substantially different to class member function lookup as suggested by other comments/answers. See this answer for an introduction to the name lookup rules for overloaded operators.
In your first example, std::wcout << a
inside X::f()
, name lookup finds:
std::wostream
has a member function operator<<
.X
, so X::operator<<
is found, and we stop here. This stage only goes up to check parent namespaces if the name is not found.std::wcout
and Y::a
, so the ADL namespaces are std
and Y
.So the overload set consists of:
std::wostream::operator<<
.X::operator<<
.std::operator<<
Y::operator<<
(or would, if there were any).and nothing else.
None of those find a match for argument type Y::A
so compilation fails.
When you remove X::operator<<
, then the unqualified lookup step finds nothing in X
so it looks in the parent namespace, recursively. Then ::operator<<
is found and that function goes into the overload set, and compilation succeeds.
To avoid this problem, the usual procedure is to put free overloaded operators of user-defined types into the same namespace as the type was defined, so in this case you would do:
namespace Y
{
inline std::wostream & operator<<(std::wostream & stream, const A & msg) { .... }
}
and then the ADL step would find this function even though the unqualified lookup step also finds X::operator<<
.
In your second example the exact meaning of using namespace Z;
is:
During unqualified name lookup, the names appear as if they were declared in the nearest enclosing namespace which contains both the using-directive and the nominated namespace.
The nearest enclosing namespace containing both X
and Z
is the global namespace, so the names behave as if in the global namespace for the unqualified lookup phase.
Therefore the process is not substantially different to my analysis for your first case, and only X::operator<<
is found by unqualified lookup. Again, this would be fixed by including the desired overload in Y
so that it is found by ADL.
Upvotes: 1
Reputation: 54029
This behavior is actually expected in C++, in order to avoid unexpected behaviors introduced by different overloads in different namespaces.
This is called name hiding. You can read a really good answer on the subject here: https://stackoverflow.com/a/1629074/182676
So overloads in different namespaces will hide each other.
You can fix this by making the correct overload visible to the compiler with using
:
Y::A a;
using Z::operator<<;
std::wcout << a;
Upvotes: 1