简体   繁体   English

重载命名空间中的运算符和 C++17 中的子命名空间是不明确的

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

I try to overload the operator<< in a namespace.我尝试在命名空间中重载operator<< Additionally, I want to include a debug namespace within the first namespace, where the operator<< does more.此外,我想在第一个命名空间中包含一个调试命名空间,其中operator<<执行更多操作。

In the main function, I create an object of a class within the first namespace and give it out with std::cout.在主 function 中,我在第一个命名空间内创建了 class 的 object,并使用 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.我希望必须先“命名”操作员,然后才能这样做,就像using test::operator<<一样,但我不必这样做。

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:查找std::cout << a将找到两个候选者:

  • test::debug::operator<<(ostream&, A const&) through regular unqualified lookup, found through the using-declaration. test::debug::operator<<(ostream&, A const&)通过常规非限定查找,通过 using-declaration 找到。
  • test::operator<<(ostream&, A const&) through argument-dependent lookup (ADL), since A is in namespace test so we find the candidates there. test::operator<<(ostream&, A const&)通过参数相关查找(ADL),因为A在命名空间test中,所以我们在那里找到候选者。

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 .在我看来,最明智的做法实际上是包装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.其中debug只是一种类型,它具有对A的成员引用,并且具有比平常更详细的自定义日志记录。

I expected to have to 'name' the operator before I can do that, as in using test::operator<<, but I don't have to.我希望必须先“命名”操作员,然后才能这样做,就像使用 test::operator<< 一样,但我不必这样做。

This is because of Argument Dependent Lookup (ADL) .这是因为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.如果通过 using 将操作符从debug命名空间拉到当前的 scope 中,这不会“覆盖”现有的操作符,它们都是可用的,因此存在歧义。

There are many ways to deal with it, one possibility is to use a different type for the debug output:有很多方法可以处理它,一种可能是使用不同的类型进行调试 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';

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

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