Visual studio 19.00.23506 and g ++ 5.4.0 normally compile the code below, but here clang 3.8.0 gives an error. I don’t understand what he doesn’t like and how to rewrite the code so that “this” understands it.

Code:

#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; }; } // XXX std::wostream& operator<<(std::wostream& stream, const XXX::A& a) { stream << "x=" << ax; return stream; } std::wostream& operator<<(std::wostream& stream, const XXX::B& b) { stream << "a=("<< ba <<")"; return stream; } int main() { boost::optional<XXX::B> b; std::wcout << b; } 

The code in the online compiler is here .

Compiler log:

 Error(s): source_file.cpp:10:10: error: call to function 'operator<<' that is neither visible in the template definition nor found by argument-dependent lookup stream << *value; ^ source_file.cpp:47:16: note: in instantiation of function template specialization 'operator<<<XXX::B>' requested here std::wcout << b; ^ source_file.cpp:37:16: note: 'operator<<' should be declared prior to the call site or in namespace 'XXX' std::wostream& operator<<(std::wostream& stream, const XXX::B& b) ^ 1 error generated. 
  • note: 'operator<<' should be declared prior to the call : AlexDenisov
  • Yes, I myself can translate, that's just how to fix it is not clear. VC and GCC are not against that. - ffk
  • Here, the rather big question is why the GCC accepts this. - AnT 2:17 pm

2 answers 2

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:

  1. 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.

  2. 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 .

  • So you need to transfer. Thank. - ffk pm

Try this:

 #include <boost/optional.hpp> #include <sstream> #include <iostream> namespace XXX { struct A { int x = 1; }; struct B { A a; }; } // XXX std::wostream& operator<<(std::wostream& stream, const XXX::A& a) { stream << "x=" << ax; return stream; } std::wostream& operator<<(std::wostream& stream, const XXX::B& b) { stream << "a=("<< ba <<")"; return stream; } template<class T> std::wostream& operator<<(std::wostream& stream, const boost::optional<T>& value) { if (value) { stream << *value; } else { stream << "none"; } return stream; } int main() { boost::optional<XXX::B> b; std::wcout << b; } 

For details, it is better to go here: https://clang.llvm.org/compatibility.html There are just examples with all the errors.

  • Unfortunately I can not rearrange. The template operator << comes from a .h file, which can be connected before declaring a local operator << for A and B types and is needed not to write an option for each type with boost :: optional, boost :: shared_ptr, etc. - ffk
  • You can make 1 more h-file with A and B and operator << and connect up to .h with operator <<. - Axenow