You operator << is called from the function template
template<class T> std::wostream& operator<<(std::wostream& stream, const boost::optional<T>& value)
in a context that is dependent , i.e. depends on the template parameter T
Such calls are resolved through two phases of the search for names:
The usual name lookup, which is done from the context of the definition of your template function.
In your example, there is nothing suitable from there, because you have not announced anything suitable over the template definition.
ADL search (argument-dependent lookup), which is done from the context of calling your template function (in main ). Additional declarations may be visible from this place, but at the same time these additional declarations are searched only in associated namespaces (associated with the arguments of your call).
In your example, the associated namespaces are std and XXX . In them, in the context of a challenge, nothing new and suitable is visible. The global namespace in your case is not an associate, therefore, what you additionally declared there will not be found via ADL.
Your global operators
std::wostream& operator<<(std::wostream& stream, const XXX::A& a); std::wostream& operator<<(std::wostream& stream, const XXX::B& b);
not found, the code is not compiled.
Or, declare your additional statements above the function template declarations so that they are in step 1. Or make them XXX members, so that they are in step 2 (you can leave them "below").
The second option is more logical. Why did you even decide to litter the global namespace with these functions that have nothing to do with it?
#include <boost/optional.hpp> #include <sstream> #include <iostream> template<class T> std::wostream& operator<<(std::wostream& stream, const boost::optional<T>& value) { if (value) { stream << *value; } else { stream << "none"; } return stream; } namespace XXX { struct A { int x = 1; }; struct B { A a; }; std::wostream& operator<<(std::wostream& stream, const A& a) { stream << "x=" << ax; return stream; } std::wostream& operator<<(std::wostream& stream, const B& b) { stream << "a=("<< ba <<")"; return stream; } } // XXX int main() { boost::optional<XXX::B> b; std::wcout << b; }
The question of why GCC suddenly agreed to compile your original version of the code is not closed. I minimized the example and asked the appropriate question on EnSO
GCC and ADL for operators in expressions
It turns out that this is a known GCC bug ( bug 51577 , as well as a related bug 70099 ), which manifests itself when invoking overloaded operators through the usual operator syntax. That is, Clang is absolutely right here, rejecting your version of the code.
VC, on the other hand, does not implement any sane "two-phase" search for names, but simply sees everything indiscriminately, as can be seen from the call point in main .
note: 'operator<<' should be declared prior to the call: AlexDenisov