运算符重载,名称解析和名称空间 [英] Operator overloading, name resolution and namespaces
问题描述
我希望对涉及ADL,名称空间和运算符重载的令人困惑的情况有所了解.
I would like some light to be shed on a puzzling situation involving ADL, namespaces and operator overloading.
让Foo成为一个在其自己的名称空间中定义一个类(Deriv
)以及返回另一个类的模板化operator *
的库.
Let Foo be a library which defines a class ( Deriv
) in its own namespace, along with a templated operator *
which returns another class.
namespace Foo {
class Deriv {};
class Another {};
template <typename T>
Another operator* ( T x, const Deriv& d ) { return Another();}
}
现在,我在自己的库Bar中使用Foo的类,该库定义了另一个operator *
,这次仅用于float
.
Now I use Foo's class in my own library Bar, which defines another operator *
, this time only for float
.
namespace Bar {
typedef Foo::Deriv MyDeriv;
MyDeriv operator* (float x, const MyDeriv& d) { return MyDeriv();}
}
我观察到编译器行为的不同,具体取决于是否在namespace Bar
内部.
I observe a difference in compiler behaviour depending whether one is inside namespace Bar
or not.
此功能(Bar::f1
)使用operator *
的第二版进行编译:
This function (Bar::f1
) compiles, using the second version of the operator *
:
namespace Bar {
void f1() {
Bar::MyDeriv a;
Bar::MyDeriv b = 3.f * a;
}
}
,但名称空间栏(f2()
)外部的同一函数无法编译,因为编译器仅尝试使用Foo::operator*
,并且无法猜测它必须使用Bar::operator*
.
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*
.
void f2() {
Bar::MyDeriv a;
Bar::MyDeriv b = 3.f * a; // Error : cannot convert Foo:Another to Bar::Myderiv
}
您可以在此处查看代码: http://ideone.com/pkPeOY
You can see the code live here :http://ideone.com/pkPeOY
现在,如果未将Foo::operator*
模板化并定义为Foo::operator*(float, const Deriv& d);
,则两个函数均无法编译,并出现相同错误(歧义运算符重载),如此处所示: http://ideone.com/wi1EWS
Now, if Foo::operator*
was not templated and defined as 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
所以,面对这种情况,这就是困扰我的地方
So, facing this situation, this is what is puzzling me
-
在模板化情况下,编译
f2
时,编译器会考虑使用Foo::operator*
而不是Bar::operator*
,而在非模板化中在这种情况下,它会考虑同时使用两者(并且由于模棱两可而拒绝走得更远). 是什么使编译器的行为有所不同?
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). What makes the compiler behave differently ?
我的图书馆Bar的用户将位于Bar::
命名空间之外,但我希望使用Bar::operator*
,而不是Foo::operator*
.我考虑过显式地调用丑陋的Bar::operator*(3.f,a)
,或在全局名称空间中插入自己的运算符,我认为这是事物. 我是否缺少选项,或者我做错了什么?
A user of my library Bar will be outside the Bar::
namespace, yet I want Bar::operator*
to be used, and not 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. Is there an option I am missing, or am I doing something wrong ?
推荐答案
在有模板的情况下,编译
f2
时,编译器考虑使用Foo::operator*
而不是Bar::operator*
,而在没有模板的情况下,它考虑同时使用(并且由于歧义而拒绝继续使用) .是什么使编译器的行为有所不同?
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). What makes the compiler behave differently ?
在这两种情况下,编译器都考虑同时使用这两种方法,但是在使用模板化的operator*
的情况下,该调用不会产生歧义,因为存在一个非模板化的函数,该函数的参数类型与参数完全匹配(请尝试用3.f
替换3.f
3.
,您将看到找到模板版本).通常:
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). Typically:
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::
命名空间之外,但我希望使用Bar::operator*
,而不是Foo :: operator *.我考虑过显式地调用丑陋的Bar::operator*(3.f,a)
,或在全局名称空间中插入自己的运算符,我认为这是一件坏事.是否有我遗漏的选项,或者我做错了什么?
A user of my library Bar will be outside the
Bar::
namespace, yet I wantBar::operator*
to be used, and not 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. Is there an option I am missing, or am I doing something wrong ?
不幸的是,ADL找不到您的重载,因为operator*
的唯一参数是在名称空间Foo
中定义的float
和MyDeriv
.一种可能的方法是从Foo::Deriv
继承:
Unfortunately, ADL will not find your overload since the only parameters of operator*
are float
and MyDeriv
which are defined inside the namespace Foo
. One possible way would be to inherit from Foo::Deriv
:
namespace Bar {
struct MyDeriv: public Foo::Deriv {};
MyDeriv operator* (float x, const MyDeriv& d) { return MyDeriv();}
}
另一种方法是在Foo
名称空间内声明operator*
的重载:
Another one is to declare your overload for operator*
inside the Foo
namespace:
namespace Bar {
typedef Foo::Deriv MyDeriv;
}
namespace Foo {
Bar::MyDeriv operator* (float x, const Bar::MyDeriv& d) { return Bar::MyDeriv(); }
}
这篇关于运算符重载,名称解析和名称空间的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!