Search code examples
c++namespacesostream

no match for operator<<(std::ostream, ns::Type) when in the global namespace


Can someone explain why the << operator cannot be found when it's in the global namespace? Also, why might it be a problem if it could be?

#include <ostream>
#include <iostream>

namespace ns {
struct Bar { int x; };
struct Foo { Bar bar; };
};

// Why does this fix things
#if 0
inline std::ostream& operator<<(std::ostream& os, const ns::Bar& bar);
inline std::ostream& operator<<(std::ostream& os, const ns::Foo& foo);
#endif

template<class T>
std::ostream& printer(std::ostream& os, const T& obj)
{
    os << obj; // error: no match for 'operator<<'
    return os;
}

// I do not own 'ns' but I want to make a generic printer for it
// Wrapping this in 'namespace ns {...}' is the solution, but why?
inline std::ostream& operator<<(std::ostream& os, const ns::Bar& bar)
{
    return printer(os, bar.x);
}

inline std::ostream& operator<<(std::ostream& os, const ns::Foo& foo)
{
    return printer(os, foo.bar);
}

void test()
{
    ns::Foo foo;
    std::cout << foo;
}

https://godbolt.org/z/8bf1EaWPq

I recently hit this after upgrading my OS, which is now on gcc-13 (probably coming from gcc-11). There were no errors before.

At first this might look like an include order problem, but the operator is defined by the time the template is instantiated.


Solution

  • This is because of argument-dependent lookup (ADL) does not look into the global namespace, only into the namespace of the types involved. When os << obj; is called inside printer, the compiler tries to find an appropriate operator<< in :

    1. The global namespace.
    2. The namespace of std::ostream (std).
    3. The namespace of T (which is ns for ns::Bar and ns::Foo).

    But because the operator<< definitions are in the global namespace, ADL does not look there because ns::Foo and ns::Bar are from ns, and ADL prioritizes looking inside the ns namespace.

    Solution:

    Define operator<< inside namespace ns

        namespace ns 
        {
          inline std::ostream& operator<<(std::ostream& os, const Bar& bar) {
            return printer(os, bar.x);
          }
    
         inline std::ostream& operator<<(std::ostream& os, const Foo& foo) {
            return printer(os, foo.bar);
          }
       }
    

    Why does MSVC accept it while GCC/Clang reject it?

    MSVC is more permissive in some ADL cases and sometimes searches additional namespaces. GCC is stricter and follow the standard more rigorously, which is why your code fails there. Maybe in other words, got stricter nowadays.

    Why did this work in GCC 11 but break in GCC 13?

    Newer versions of GCC may have improved conformance with C++ lookup rules, leading to stricter ADL behavior. GCC 11 might have allowed an implicit lookup from the global namespace, but GCC 13 follows stricter rules.