[英]Operator overloading, name resolution and namespaces
我想對涉及ADL,名稱空間和運算符重載的令人費解的情況有所了解。
讓Foo成為一個庫,它在自己的命名空間中定義一個類( Deriv
),以及一個返回另一個類的模板化operator *
。
namespace Foo {
class Deriv {};
class Another {};
template <typename T>
Another operator* ( T x, const Deriv& d ) { return Another();}
}
現在我在我自己的庫Bar中使用Foo的類,它定義了另一個operator *
,這次只用於float
。
namespace Bar {
typedef Foo::Deriv MyDeriv;
MyDeriv operator* (float x, const MyDeriv& d) { return MyDeriv();}
}
我發現編譯器行為的不同取決於是否在namespace Bar
。
此函數( Bar::f1
)使用operator *
的第二個版本進行編譯:
namespace Bar {
void f1() {
Bar::MyDeriv a;
Bar::MyDeriv b = 3.f * a;
}
}
雖然命名空間Bar( f2()
)之外的相同函數無法編譯,因為編譯器只嘗試使用Foo::operator*
並且無法猜測它必須使用Bar::operator*
。
void f2() {
Bar::MyDeriv a;
Bar::MyDeriv b = 3.f * a; // Error : cannot convert Foo:Another to Bar::Myderiv
}
您可以在此處查看代碼: http : //ideone.com/pkPeOY
現在,如果Foo::operator*
沒有模板化並定義為Foo::operator*(float, const Deriv& d);
然后這兩個函數都無法使用相同的錯誤進行編譯(模糊運算符重載),如下所示: http : //ideone.com/wi1EWS
所以,面對這種情況,這讓我感到困惑
在模板化的情況下,編譯
f2
,編譯器會考慮使用Foo::operator*
而不是Bar::operator*
,而在非模板化的情況下,它會考慮使用兩者(並且由於模糊性而拒絕進一步使用)。 是什么讓編譯器的行為有所不同?
在這兩種情況下,編譯器都考慮使用兩者,但在模板化operator*
的情況下,調用不是模糊的,因為有一個非模板化函數,參數類型與參數完全匹配(嘗試將3.f
替換為3.
和你會看到找到模板化的版本)。 典型:
template <typename T>
void g (T) { }
void g (float) { }
g(0.f); // Ok, the overload for float is preferred over the templated version
我的庫Bar的用戶將在
Bar::
namespace之外,但我想要使用Bar::operator*
,而不是Foo :: operator *。 我考慮過明確地調用Bar::operator*(3.f,a)
,這是丑陋的,或者在全局命名空間中插入我自己的運算符,我認為這是一件壞事。 有沒有我想念的選擇,或者我做錯了什么?
不幸的是,ADL不會找到你的重載,因為operator*
的唯一參數是float
和MyDeriv
,它們在命名空間Foo
中定義。 一種可能的方法是從Foo::Deriv
繼承:
namespace Bar {
struct MyDeriv: public Foo::Deriv {};
MyDeriv operator* (float x, const MyDeriv& d) { return MyDeriv();}
}
另一個是在Foo
命名空間內為operator*
聲明重載:
namespace Bar {
typedef Foo::Deriv MyDeriv;
}
namespace Foo {
Bar::MyDeriv operator* (float x, const Bar::MyDeriv& d) { return Bar::MyDeriv(); }
}
聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.