[英]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)放在同一個命名空間中?
根據其他答案,我最終推斷出運算符<<的ADL受到它在另一個運算符<<內部發生的事實的阻礙。
今天的教訓:總是根據編寫器方法編寫運算符<< overload
這是修復:
#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;
}
在Argument Depended Lookup(或Koenig Lookup)中,編譯器將可見性范圍添加到在每個參數的父作用域中聲明的所有符號。
即使Y
是X
“子命名空間”,它們在ADL
方面也不相關。 您的第一個參數是在std::
namespace中定義的類型,而第二個是本地符號(在與函數本身相同的命名空間中定義)。
請注意,由於上述原因,您很可能會在此行中收到另一個錯誤:
std::cout << v << std::endl;
當編譯器無法為std::vector<double>
找到operator<<
overloaded(因為它位於namespace X
)。
要解決此問題,您可以使用:
using X::operator<<
在namespace Y
或移動該重載。
如果你想知道,為什么foobar
示例有效:那是因為ADL
( Argument Dependent Lookup)是關於函數參數的范圍,而不是函數本身。 在foobar
代碼中,不應用ADL
。
就這么簡單: 為了使函數重載,重載版本必須存在於同一個nemaspace中,否則,是一個完全不同的函數。 函數的名稱(對於編譯器)是從全局名稱空間到函數本身的完整路徑。
::function_at_global_namespace();
Namespace::function_name(); // Some funtion within a namespace;
Namespace_1::function_name(); // Some other function within another namespace;
所以,
Standar std::ostream& operator<<
存在於std
命名空間中,你沒有重載那個操作符,只是在命名空間X
定義另一個。
正如@ 0x499602D2所指出的那樣,你必須在命名空間Y
中使用X::operator<<
才能調用該版本的運算符。
std::ostream& std::operator<<
和std::ostream& X::operator<<
是不同的函數。
在下面的代碼中,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; } }
聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.