[英]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.