c++namespacesoperator-overloadingambiguous-call

Multiple namespaces overloaded operator<<, how can I use them?


I'm reading a book. Obviously there are some failures in it.

In the code below, how can I access plant::operator<< in run() and plant::debug::operator<< in diagnostic()? The compiler can't decide between multiple operator<< in method diagnostic(). How can I tell it which one to use?

#include <iostream>
#include <string>

namespace plant {
    class Tree
    {
        std::string name_;
        public:
        explicit Tree(const std::string_view name) : name_{name} {}
        void print(std::ostream& os) const { os << name_; }
 
    };

    std::ostream& operator<<(std::ostream& os, const Tree& arg)
    {
        arg.print(os); return os;
    }
    namespace examples {

        }
    namespace debug {
        std::ostream& operator<<(std::ostream& os, const Tree& arg)
        {
            os << "DEBUG: .."; arg.print(os); return os;
        }    
    }
}

plant::Tree baum{"Mein Baum"};
void run()
{
    using namespace plant;
    std::cout << baum << "\n";
}

void diagnostic()
{
    using namespace plant::debug;
    std::cout << baum << "\n";
}

int main() {
    run();
    diagnostic();
}

Solution

  • Inside of diagnostic(), both operator<<'s are found as candidates, hence the ambiguity. plant::operator<< is found due to Argument-Dependent Lookup (which makes the using in run() redundant), and plant::debug::operator<< is found due to its using statement.

    To resolve the ambiguity, you could call the debug::operator<< directly, eg:

    void diagnostic()
    {
        //using namespace plant::debug;
        plant::debug::operator<<(std::cout, baum) << "\n";
    }
    

    However, that kind of defeats the purpose of defining an operator<< in the first place, rather than a standalone function.

    One option is you can instead simply move plant::operator<< into its own namespace, and then update run() to use that namespace, eg:

    namespace plant {
        class Tree
        {
            ...
        };
    
        namespace no_debug {
            std::ostream& operator<<(std::ostream& os, const Tree& arg)
            {
                ...
                return os;
            }    
        }
        namespace debug {
            std::ostream& operator<<(std::ostream& os, const Tree& arg)
            {
                os << "DEBUG: ..";
                ...
                return os;
            }    
        }
    }
    
    ...
    
    void run()
    {
        using namespace plant::no_debug;
        std::cout << baum << "\n";
    }
    
    void diagnostic()
    {
        using namespace plant::debug;
        std::cout << baum << "\n";
    }
    

    Online Demo