在 C++17 中重载命名空间和子命名空间中的运算符是不明确的
Overloading an operator in a namespace and a sub-namespace in C++17 is ambiguous
我尝试在命名空间中重载 operator<<
。此外,我想在第一个命名空间中包含一个调试命名空间,其中 operator<<
做的更多。
在主函数中,我在第一个命名空间中创建了一个class的对象,并用std::cout给出了它。我希望在我可以这样做之前必须 'name' 运算符,如 using 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";
}
}
I expected to have to 'name' the operator before I can do that, as in using test::operator<<, but I don't have to.
这是因为Argument Dependent Lookup (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 << a
将找到两个候选项:
test::debug::operator<<(ostream&, A const&)
通过常规不合格查找,通过using-declaration找到。
test::operator<<(ostream&, A const&)
通过依赖于参数的查找 (ADL),因为 A
在命名空间 test
中,所以我们在那里找到候选人。
这两个候选人的签名相同,根本没有什么区别,所以是模棱两可的。
在我看来,最明智的做法实际上是包装 a
。写入:
std::cout << debug{a} << '\n';
其中 debug
只是一个类型,它具有对 A
的成员引用,并且具有比平常更详细的自定义日志记录。
我尝试在命名空间中重载 operator<<
。此外,我想在第一个命名空间中包含一个调试命名空间,其中 operator<<
做的更多。
在主函数中,我在第一个命名空间中创建了一个class的对象,并用std::cout给出了它。我希望在我可以这样做之前必须 'name' 运算符,如 using 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";
}
}
I expected to have to 'name' the operator before I can do that, as in using test::operator<<, but I don't have to.
这是因为Argument Dependent Lookup (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 << a
将找到两个候选项:
test::debug::operator<<(ostream&, A const&)
通过常规不合格查找,通过using-declaration找到。test::operator<<(ostream&, A const&)
通过依赖于参数的查找 (ADL),因为A
在命名空间test
中,所以我们在那里找到候选人。
这两个候选人的签名相同,根本没有什么区别,所以是模棱两可的。
在我看来,最明智的做法实际上是包装 a
。写入:
std::cout << debug{a} << '\n';
其中 debug
只是一个类型,它具有对 A
的成员引用,并且具有比平常更详细的自定义日志记录。