`std::cout << FooStuff::Foo();` 选择完全不相关的重载而不是完全匹配的重载
`std::cout << FooStuff::Foo();` chooses completely unrelated overload instead of exactly matching one
我有这个代码:
#include <iostream>
namespace FooStuff
{
struct Foo { };
}
decltype(std::cout)& operator << (decltype(std::cout)& left, const FooStuff::Foo& right)
{
return left;
}
void test1()
{
// This works fine
std::cout << FooStuff::Foo();
}
据我所知,这是最好的 operator <<
可以写下来以匹配 test1
中的调用,并且它的工作方式与您预期的一样。
现在在上面的代码下面添加这段代码:
namespace BarStuff
{
struct Bar { };
// Danger zone
void test2()
{
// This works too, for now
std::cout << FooStuff::Foo();
}
}
test2
中的调用也如您所料。
但是如果我在“危险区域”注释的正下方插入以下运算符,一切都会中断:
Bar operator << (Bar left, Bar right)
{
return left;
}
现在 test2
中的调用不会编译,因为编译器选择了完全不合适的重载,它需要一堆 Bar
s,即使第一个片段中的运算符应该是完美的匹配:
main.cpp:33: error: invalid operands to binary expression ('std::ostream' (aka 'basic_ostream<char>') and 'FooStuff::Foo')
(A ton of unrelated overloads omitted for brevity)
main.cpp:25: candidate function not viable: no known conversion from 'std::ostream' (aka 'basic_ostream<char>') to 'BarStuff::Bar' for 1st argument
大便怎么回事?为什么编译器选择那个重载而不是我希望它选择的重载?
如果 Foo
移到 FooStuff
之外,为什么错误会消失?
当您编写 std::cout << FooStuff::Foo();
时,将进行名称查找以确定 <<
的候选者以用于重载解析。
对于运算符的重载解析,此查找有两个部分:operator<<
的非限定名称查找和 operator<<
.
的 argument-dependent 查找
对于非限定名称查找,通常情况下,查找从内部范围遍历到外部范围,并在找到匹配项后立即停止。因此,如果您在 // Danger zone
放置一个重载,BarStuff
之外的那个将被隐藏。
对于 argument-dependent 名称查找,将考虑操作数的 class 类型及其紧邻命名空间中的所有重载。在你的情况下,这意味着 struct Foo
和 namespace FooStuff
内的重载也会被发现,无论 std::cout << FooStuff::Foo();
放在哪里。
出于上述原因,运算符重载应放置在包含 class 的名称空间中,它们为此运算符重载。这确保总是通过 argument-dependent 查找找到过载。
我有这个代码:
#include <iostream>
namespace FooStuff
{
struct Foo { };
}
decltype(std::cout)& operator << (decltype(std::cout)& left, const FooStuff::Foo& right)
{
return left;
}
void test1()
{
// This works fine
std::cout << FooStuff::Foo();
}
据我所知,这是最好的 operator <<
可以写下来以匹配 test1
中的调用,并且它的工作方式与您预期的一样。
现在在上面的代码下面添加这段代码:
namespace BarStuff
{
struct Bar { };
// Danger zone
void test2()
{
// This works too, for now
std::cout << FooStuff::Foo();
}
}
test2
中的调用也如您所料。
但是如果我在“危险区域”注释的正下方插入以下运算符,一切都会中断:
Bar operator << (Bar left, Bar right)
{
return left;
}
现在 test2
中的调用不会编译,因为编译器选择了完全不合适的重载,它需要一堆 Bar
s,即使第一个片段中的运算符应该是完美的匹配:
main.cpp:33: error: invalid operands to binary expression ('std::ostream' (aka 'basic_ostream<char>') and 'FooStuff::Foo')
(A ton of unrelated overloads omitted for brevity)
main.cpp:25: candidate function not viable: no known conversion from 'std::ostream' (aka 'basic_ostream<char>') to 'BarStuff::Bar' for 1st argument
大便怎么回事?为什么编译器选择那个重载而不是我希望它选择的重载?
如果 Foo
移到 FooStuff
之外,为什么错误会消失?
当您编写 std::cout << FooStuff::Foo();
时,将进行名称查找以确定 <<
的候选者以用于重载解析。
对于运算符的重载解析,此查找有两个部分:operator<<
的非限定名称查找和 operator<<
.
对于非限定名称查找,通常情况下,查找从内部范围遍历到外部范围,并在找到匹配项后立即停止。因此,如果您在 // Danger zone
放置一个重载,BarStuff
之外的那个将被隐藏。
对于 argument-dependent 名称查找,将考虑操作数的 class 类型及其紧邻命名空间中的所有重载。在你的情况下,这意味着 struct Foo
和 namespace FooStuff
内的重载也会被发现,无论 std::cout << FooStuff::Foo();
放在哪里。
出于上述原因,运算符重载应放置在包含 class 的名称空间中,它们为此运算符重载。这确保总是通过 argument-dependent 查找找到过载。