简体   繁体   中英

Overloading an operator in a namespace and a sub-namespace in C++17 is ambiguous

I try to overload the operator<< in a namespace. Additionally, I want to include a debug namespace within the first namespace, where the operator<< does more.

In the main function, I create an object of a class within the first namespace and give it out with std::cout. I expected to have to 'name' the operator before I can do that, as in using test::operator<< , but I don't have to.

This leads to my issue: If I want to use my debug operator now, it is ambiguous and I cannot use it and I don't really understand why.

#include <iostream>
#include <string>

namespace test{
    class A{
        std::string str_;
    public:
        explicit A(const std::string& str) : str_{str} {}
        inline std::ostream& toStream(std::ostream& os) const {
            return os << str_ << "\n";
        }
    };
    std::ostream& operator<< (std::ostream& os, const A& a) {
        return a.toStream(os);
    }
}

namespace test {
    namespace debug {
        std::ostream& operator<< (std::ostream& os, const A& a) {
            std::string info = "\n\tDebug\n"
                "\t\tLine: " + std::to_string(__LINE__) + "\n"
                "\t\tFile: " __FILE__ "\n"
                "\t\tDate: " __DATE__ "\n"
                "\t\tTime: " __TIME__ "\n"
                "\t\tVersion: " + std::to_string(__cplusplus) + "\n";
            return a.toStream(os) << info;
        }
    }
}

int main(int argc, const char* argv[]) {
    test::A a{"Test"};
    if(argc > 1) {
        using test::debug::operator<<;
        // Ambiguous error
        std::cout << a << "\n";
    } else {
        // Don't need it for some reason
        // using test::operator<<;
        std::cout << a << "\n";
    }
}

When you have:

using test::debug::operator<<;
std::cout << a << "\n";

Lookup for std::cout << a is going to find two candidates:

  • test::debug::operator<<(ostream&, A const&) through regular unqualified lookup, found through the using-declaration.
  • test::operator<<(ostream&, A const&) through argument-dependent lookup (ADL), since A is in namespace test so we find the candidates there.

Those two candidates have the same signature, there's nothing at all to differentiate them, so it's ambiguous.


The sanest thing to do, in my opinion, is actually to wrap a . Write:

std::cout << debug{a} << '\n';

Where debug is just a type that has a member reference to A , and has its own custom logging that is more detailed than usual.

I expected to have to 'name' the operator before I can do that, as in using test::operator<<, but I don't have to.

This is because of Argument Dependent Lookup (ADL) .

If you pull the operator from the debug namespace into the current scope via the using, this does not "overwrite" the existing operator, they are both avaliable, hence the ambiguity.

There are many ways to deal with it, one possibility is to use a different type for the debug output:

namespace test {
    namespace debug {
        struct debug_A {
            const A& data;
            debug_out(const A& a) : a(a) {}
        };

        std::ostream& operator<< (std::ostream& os, const debug_A& d) {
            auto& a = d.data;
            std::string info = "\n\tDebug\n"
                "\t\tLine: " + std::to_string(__LINE__) + "\n"
                "\t\tFile: " __FILE__ "\n"
                "\t\tDate: " __DATE__ "\n"
                "\t\tTime: " __TIME__ "\n"
                "\t\tVersion: " + std::to_string(__cplusplus) + "\n";
            return a.toStream(os) << info;
        }
    }
}

Now you can call it via

std::cout << test::debug::debug_A{ a } << '\n';

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM