[英]Operator overloading, name resolution and namespaces
I would like some light to be shed on a puzzling situation involving ADL, namespaces and operator overloading. 我想对涉及ADL,名称空间和运算符重载的令人费解的情况有所了解。
Let Foo be a library which defines a class ( Deriv
) in its own namespace, along with a templated operator *
which returns another class. 让Foo成为一个库,它在自己的命名空间中定义一个类(
Deriv
),以及一个返回另一个类的模板化operator *
。
namespace Foo {
class Deriv {};
class Another {};
template <typename T>
Another operator* ( T x, const Deriv& d ) { return Another();}
}
Now I use Foo's class in my own library Bar, which defines another operator *
, this time only for float
. 现在我在我自己的库Bar中使用Foo的类,它定义了另一个
operator *
,这次只用于float
。
namespace Bar {
typedef Foo::Deriv MyDeriv;
MyDeriv operator* (float x, const MyDeriv& d) { return MyDeriv();}
}
I observe a difference in compiler behaviour depending whether one is inside namespace Bar
or not. 我发现编译器行为的不同取决于是否在
namespace Bar
。
This function ( Bar::f1
) compiles, using the second version of the operator *
: 此函数(
Bar::f1
)使用operator *
的第二个版本进行编译:
namespace Bar {
void f1() {
Bar::MyDeriv a;
Bar::MyDeriv b = 3.f * a;
}
}
while the same function outside namespace Bar ( f2()
) fails to compile, because the compiler attempts only to use Foo::operator*
and cannot guess that it must use Bar::operator*
. 虽然命名空间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
}
You can see the code live here : http://ideone.com/pkPeOY 您可以在此处查看代码: http : //ideone.com/pkPeOY
Now, if Foo::operator*
was not templated and defined as Foo::operator*(float, const Deriv& d);
现在,如果
Foo::operator*
没有模板化并定义为Foo::operator*(float, const Deriv& d);
then both functions fail to compile with the same error (ambiguous operator overload), as can be seen here : http://ideone.com/wi1EWS 然后这两个函数都无法使用相同的错误进行编译(模糊运算符重载),如下所示: http : //ideone.com/wi1EWS
So, facing this situation, this is what is puzzling me 所以,面对这种情况,这让我感到困惑
In the templated case, when compiling f2
, the compiler considers using Foo::operator*
but not Bar::operator*
, while in the non-templated case, it considers using both (and refuses to go further because of the ambiguity). 在模板化的情况下,编译
f2
,编译器会考虑使用Foo::operator*
而不是Bar::operator*
,而在非模板化的情况下,它会考虑使用两者(并且由于模糊性而拒绝进一步使用)。 What makes the compiler behave differently ? 是什么让编译器的行为有所不同?
A user of my library Bar will be outside the Bar::
namespace, yet I want Bar::operator*
to be used, and not Foo::operator*
. 我的库Bar的用户将在
Bar::
namespace之外,但我想要使用Bar::operator*
,而不是Foo::operator*
。 I considered explicitely calling Bar::operator*(3.f,a)
, which is ugly, or inserting my own operator in the global namespace, which I reckon is a Bad Thing . 我考虑明确地打电话
Bar::operator*(3.f,a)
这是丑陋的,或插入我自己的全局命名空间中操作,这我认为是一个坏 事情 。 Is there an option I am missing, or am I doing something wrong ? 有没有我想念的选择,或者我做错了什么?
In the templated case, when compiling
f2
, the compiler considers usingFoo::operator*
but notBar::operator*
, while in the non-templated case, it considers using both (and refuses to go further because of the ambiguity).在模板化的情况下,编译
f2
,编译器会考虑使用Foo::operator*
而不是Bar::operator*
,而在非模板化的情况下,它会考虑使用两者(并且由于模糊性而拒绝进一步使用)。 What makes the compiler behave differently ?是什么让编译器的行为有所不同?
In both cases the compiler considers using both, but in the case of a templated operator*
, the call is not ambiguous since there is a non-templated function which parameter types perfectly matches the arguments (try replace 3.f
with 3.
and you will see that the templated version is found). 在这两种情况下,编译器都考虑使用两者,但在模板化
operator*
的情况下,调用不是模糊的,因为有一个非模板化函数,参数类型与参数完全匹配(尝试将3.f
替换为3.
和你会看到找到模板化的版本)。 Typically: 典型:
template <typename T>
void g (T) { }
void g (float) { }
g(0.f); // Ok, the overload for float is preferred over the templated version
A user of my library Bar will be outside the
Bar::
namespace, yet I wantBar::operator*
to be used, and not Foo::operator*.我的库Bar的用户将在
Bar::
namespace之外,但我想要使用Bar::operator*
,而不是Foo :: operator *。 I considered explicitely callingBar::operator*(3.f,a)
, which is ugly, or inserting my own operator in the global namespace, which I reckon is a Bad Thing.我考虑过明确地调用
Bar::operator*(3.f,a)
,这是丑陋的,或者在全局命名空间中插入我自己的运算符,我认为这是一件坏事。 Is there an option I am missing, or am I doing something wrong ?有没有我想念的选择,或者我做错了什么?
Unfortunately, ADL will not find your overload since the only parameters of operator*
are float
and MyDeriv
which are defined inside the namespace Foo
. 不幸的是,ADL不会找到你的重载,因为
operator*
的唯一参数是float
和MyDeriv
,它们在命名空间Foo
中定义。 One possible way would be to inherit from Foo::Deriv
: 一种可能的方法是从
Foo::Deriv
继承:
namespace Bar {
struct MyDeriv: public Foo::Deriv {};
MyDeriv operator* (float x, const MyDeriv& d) { return MyDeriv();}
}
Another one is to declare your overload for operator*
inside the Foo
namespace: 另一个是在
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.