提问者:小点点

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


我尝试重载命名空间中的运算符&<<。 此外,我希望在第一个命名空间中包含一个调试命名空间,其中运算符&<<执行更多操作。

在main函数中,我在第一个命名空间内创建一个类的对象,并用std::cout将其提供给对象。 我预计必须先“命名”运算符,然后才能这样做,就像在中使用test::operator<<那样,但我不必这样做。

这就引出了我的问题:如果我现在想使用我的调试操作符,它是模棱两可的,我不能使用它,而且我也不明白为什么。

#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";
    }
}

共2个答案

匿名用户

我预计必须先“命名”运算符,然后才能这样做,就像使用test::operator<<<一样,但我不必这样做。

这是因为参数依赖查找(ADL)。

如果您通过using将运算符从debug命名空间拉到当前作用域中,这不会“覆盖”现有运算符,它们都是可用的,因此存在歧义。

有许多方法可以处理它,其中一种可能是使用不同类型的调试输出:

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;
        }
    }
}

现在你可以通过

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

匿名用户

当你有:

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

查找std::cout<<<; 将找到两个候选者:

  • test::debug::operator<<<(Ostream&;,一个const&;)通过常规非限定查找,通过using-declaration找到。
  • test::operator<<<(ostream&;,A const&;)通过参数相关查找(ADL),因为A位于命名空间test中,所以我们在那里找到候选项。

这两个候选人有相同的签名,根本没有什么可以区分的,所以这是不明确的。

在我看来,最明智的做法实际上是包装a。 写:

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

其中debug只是一个类型,该类型具有对a的成员引用,并且具有自己的自定义日志记录,该日志记录比通常更详细。

相关问题


MySQL Query : SELECT * FROM v9_ask_question WHERE 1=1 AND question regexp '(c++17|中|重载|命名|空间|子|命名|空|间中|运算符|不明确)' ORDER BY qid DESC LIMIT 20
MySQL Error : Got error 'repetition-operator operand invalid' from regexp
MySQL Errno : 1139
Message : Got error 'repetition-operator operand invalid' from regexp
Need Help?