我尝试重载命名空间中的运算符&<<
。 此外,我希望在第一个命名空间中包含一个调试命名空间,其中运算符&<<
执行更多操作。
在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";
}
}
我预计必须先“命名”运算符,然后才能这样做,就像使用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
的成员引用,并且具有自己的自定义日志记录,该日志记录比通常更详细。