operator<< 重载决议 (C++)
operator<< overload resolution (C++)
下面的代码在 a::b::print
函数中给出了一个错误:Invalid operands to binary expression ('std::ostream' (aka 'basic_ostream<char>') and 'Foo')
.
如果我注释掉命名空间 b
中的 operator<<
重载,错误就会消失。但我不明白为什么这会有所不同,因为它与 namspace a
中的 operator<<
重载具有不同的签名。怎么回事?!
#include <iostream>
class Foo {};
namespace a {
std::ostream &operator<<(std::ostream &os, Foo &foo);
void print(Foo& foo) {
std::cout << foo;
}
namespace b {
std::ostream &operator<<(std::ostream &os, double d); // uncomment to resolve error
void print(Foo& foo) {
std::cout << foo; // error here
}
}
}
由于命名空间 b
嵌套在命名空间 a
中,在 b
中声明的名称将隐藏在 a
中声明的名称。当在 a::b::print
中进行名称查找时,它会搜索 b
,并且 如果没有找到它要查找的内容, 会继续在 a
中搜索。所以它确实在 b
中找到了 operator<< 并停止寻找,甚至没有考虑 a
中的正确运算符。当你注释掉它时,它在 b
中找不到它,并继续搜索 a
并找到它。您可以通过在命名空间 b:
中添加它来解决此问题
using a::operator<<;
但是,您的代码的真正问题是您没有正确使用 ADL(参数相关查找)。处理用户定义类型的运算符应该与它们所处理的类型位于 相同的命名空间 中。 ADL 所做的是添加额外的命名空间以进行搜索,以便调用中涉及的任何参数(或模板参数)都将自动考虑其命名空间。 (这就是为什么当您的代码在 std
之外时,您可以使用 std
命名空间中提供的运算符用于内置类型。)
因此将 operator<<
移出命名空间 a
并移入 Foo
所在的同一(全局)命名空间。或者,可能更好,将 Foo
移动到命名空间 a
.
下面的代码在 a::b::print
函数中给出了一个错误:Invalid operands to binary expression ('std::ostream' (aka 'basic_ostream<char>') and 'Foo')
.
如果我注释掉命名空间 b
中的 operator<<
重载,错误就会消失。但我不明白为什么这会有所不同,因为它与 namspace a
中的 operator<<
重载具有不同的签名。怎么回事?!
#include <iostream>
class Foo {};
namespace a {
std::ostream &operator<<(std::ostream &os, Foo &foo);
void print(Foo& foo) {
std::cout << foo;
}
namespace b {
std::ostream &operator<<(std::ostream &os, double d); // uncomment to resolve error
void print(Foo& foo) {
std::cout << foo; // error here
}
}
}
由于命名空间 b
嵌套在命名空间 a
中,在 b
中声明的名称将隐藏在 a
中声明的名称。当在 a::b::print
中进行名称查找时,它会搜索 b
,并且 如果没有找到它要查找的内容, 会继续在 a
中搜索。所以它确实在 b
中找到了 operator<< 并停止寻找,甚至没有考虑 a
中的正确运算符。当你注释掉它时,它在 b
中找不到它,并继续搜索 a
并找到它。您可以通过在命名空间 b:
using a::operator<<;
但是,您的代码的真正问题是您没有正确使用 ADL(参数相关查找)。处理用户定义类型的运算符应该与它们所处理的类型位于 相同的命名空间 中。 ADL 所做的是添加额外的命名空间以进行搜索,以便调用中涉及的任何参数(或模板参数)都将自动考虑其命名空间。 (这就是为什么当您的代码在 std
之外时,您可以使用 std
命名空间中提供的运算符用于内置类型。)
因此将 operator<<
移出命名空间 a
并移入 Foo
所在的同一(全局)命名空间。或者,可能更好,将 Foo
移动到命名空间 a
.