为什么我不能将此运算符重载放在与结构相同的名称空间中?
Why I cannot put this operator overload in the same namespace as the struct?
我有以下代码:
#include <iostream>
#include <vector>
namespace X {
std::ostream& operator<<(std::ostream& os,const std::vector<double>& v){
for (int i=0;i<v.size();i++){os << v[i] << " ";}
return os;
}
namespace Y {
struct A {std::vector<double> x;};
std::ostream& operator<<(std::ostream& os,const A& a){
os << a.x << std::endl;
return os;
}
}
}
using namespace X;
int main(int argc, char** argv) {
std::vector<double> v(10,0);
std::cout << v << std::endl;
Y::A a;
std::cout << a << std::endl;
return 0;
}
第一个重载有效,但第二个无效。由于某种原因,它找不到第一个。我收到错误:
no match for 'operator<<' (operand types are 'std::ostream
{aka std::basic_ostream<char>}' and 'const std::vector<double>')
os << a.x << std::endl;
^
我不明白为什么会出现此错误。例如这样的事情似乎是完全有效的:
namespace A {
void foo(){}
namespace B {
void bar(){foo();}
}
}
但是,解决上述问题的唯一方法是将第二个重载也放在 X 中。为什么不能将它放在与结构相同的命名空间中(即 X::Y)?
PS:我正在阅读 ADL and I found some related questions (e.g. this and this,但我从阅读这篇文章中了解到,以上内容应该有效。
根据其他答案,我最终推断出 operator<< 的 ADL 受到阻碍,因为它发生在另一个 operator<< 中。
今天的课程:始终根据编写器方法编写 operator<< 重载 :)
修复方法如下:
#include <iostream>
#include <vector>
namespace X
{
std::ostream& operator<<(std::ostream& os,const std::vector<double>& v){
for (int i=0;i<v.size();i++){os << v[i] << " ";}
return os;
}
namespace Y
{
struct A
{
std::vector<double> x;
void write(std::ostream&os) const {
os << x << std::endl;
}
};
std::ostream& operator<<(std::ostream& os,const A& a)
{
a.write(os);
return os;
}
}
}
using namespace X;
int main(int argc, char** argv)
{
std::vector<double> v(10,0);
std::cout << v << std::endl;
X::Y::A a;
std::cout << a << std::endl;
return 0;
}
就这么简单:为了重载一个函数,重载的版本必须在同一个 nemaspace 中,否则就是一个完全不同的函数。 函数的名称(对于编译器)是从全局命名空间到函数本身的完整路径。
::function_at_global_namespace();
Namespace::function_name(); // Some funtion within a namespace;
Namespace_1::function_name(); // Some other function within another namespace;
所以,
标准 std::ostream& operator<<
存在于 std
命名空间中,您没有重载该运算符,只需在命名空间 X
.
中定义另一个运算符
正如@0x499602D2 所指出的,您必须在命名空间 Y
中使用 X::operator<<
才能调用该版本的运算符。
std::ostream& std::operator<<
和std::ostream& X::operator<<
是不同的函数。
在下面的代码中 none foo 版本正在重载。
// What version of foo gets called? A::foo, or B::foo?
namespace A {
void foo(){cout << "A::foo" << endl;}
namespace B {
void foo(){ cout << "B::foo" << endl;}
void bar(){foo();}
}
}
namespace C { void foo(int a) { cout << "C:foo" << endl; } }
在 Argument Depended Lookup(或 Koenig Lookup)中,编译器将在每个 参数.
的父范围内声明的所有符号添加到可见范围
即使 Y
是 X
的 "child namespace",它们在 ADL
方面也不相关。您的第一个参数是在 std::
命名空间中定义的类型,而第二个是局部符号(在与函数本身相同的命名空间中定义)。
请注意,由于上述原因,您很可能会在此行中遇到另一个错误:
std::cout << v << std::endl;
编译器将无法找到为 std::vector<double>
重载的 operator<<
(因为它位于 namespace X
中)。
要解决这个问题,您可以使用:
using X::operator<<
在 namespace Y
内或移动该过载。
如果您想知道,为什么 foobar
示例有效:那是因为 ADL
(Argument Dependent Lookup) 是关于函数参数的范围,而不是自己发挥作用。在 foobar
代码中,ADL
未应用。
我有以下代码:
#include <iostream>
#include <vector>
namespace X {
std::ostream& operator<<(std::ostream& os,const std::vector<double>& v){
for (int i=0;i<v.size();i++){os << v[i] << " ";}
return os;
}
namespace Y {
struct A {std::vector<double> x;};
std::ostream& operator<<(std::ostream& os,const A& a){
os << a.x << std::endl;
return os;
}
}
}
using namespace X;
int main(int argc, char** argv) {
std::vector<double> v(10,0);
std::cout << v << std::endl;
Y::A a;
std::cout << a << std::endl;
return 0;
}
第一个重载有效,但第二个无效。由于某种原因,它找不到第一个。我收到错误:
no match for 'operator<<' (operand types are 'std::ostream
{aka std::basic_ostream<char>}' and 'const std::vector<double>')
os << a.x << std::endl;
^
我不明白为什么会出现此错误。例如这样的事情似乎是完全有效的:
namespace A {
void foo(){}
namespace B {
void bar(){foo();}
}
}
但是,解决上述问题的唯一方法是将第二个重载也放在 X 中。为什么不能将它放在与结构相同的命名空间中(即 X::Y)?
PS:我正在阅读 ADL and I found some related questions (e.g. this and this,但我从阅读这篇文章中了解到,以上内容应该有效。
根据其他答案,我最终推断出 operator<< 的 ADL 受到阻碍,因为它发生在另一个 operator<< 中。
今天的课程:始终根据编写器方法编写 operator<< 重载 :)
修复方法如下:
#include <iostream>
#include <vector>
namespace X
{
std::ostream& operator<<(std::ostream& os,const std::vector<double>& v){
for (int i=0;i<v.size();i++){os << v[i] << " ";}
return os;
}
namespace Y
{
struct A
{
std::vector<double> x;
void write(std::ostream&os) const {
os << x << std::endl;
}
};
std::ostream& operator<<(std::ostream& os,const A& a)
{
a.write(os);
return os;
}
}
}
using namespace X;
int main(int argc, char** argv)
{
std::vector<double> v(10,0);
std::cout << v << std::endl;
X::Y::A a;
std::cout << a << std::endl;
return 0;
}
就这么简单:为了重载一个函数,重载的版本必须在同一个 nemaspace 中,否则就是一个完全不同的函数。 函数的名称(对于编译器)是从全局命名空间到函数本身的完整路径。
::function_at_global_namespace();
Namespace::function_name(); // Some funtion within a namespace;
Namespace_1::function_name(); // Some other function within another namespace;
所以,
标准 std::ostream& operator<<
存在于 std
命名空间中,您没有重载该运算符,只需在命名空间 X
.
正如@0x499602D2 所指出的,您必须在命名空间 Y
中使用 X::operator<<
才能调用该版本的运算符。
std::ostream& std::operator<<
和std::ostream& X::operator<<
是不同的函数。
在下面的代码中 none foo 版本正在重载。
// What version of foo gets called? A::foo, or B::foo?
namespace A {
void foo(){cout << "A::foo" << endl;}
namespace B {
void foo(){ cout << "B::foo" << endl;}
void bar(){foo();}
}
}
namespace C { void foo(int a) { cout << "C:foo" << endl; } }
在 Argument Depended Lookup(或 Koenig Lookup)中,编译器将在每个 参数.
的父范围内声明的所有符号添加到可见范围即使 Y
是 X
的 "child namespace",它们在 ADL
方面也不相关。您的第一个参数是在 std::
命名空间中定义的类型,而第二个是局部符号(在与函数本身相同的命名空间中定义)。
请注意,由于上述原因,您很可能会在此行中遇到另一个错误:
std::cout << v << std::endl;
编译器将无法找到为 std::vector<double>
重载的 operator<<
(因为它位于 namespace X
中)。
要解决这个问题,您可以使用:
using X::operator<<
在 namespace Y
内或移动该过载。
如果您想知道,为什么 foobar
示例有效:那是因为 ADL
(Argument Dependent Lookup) 是关于函数参数的范围,而不是自己发挥作用。在 foobar
代码中,ADL
未应用。