我希望能够解决涉及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*
,而在非-templated 案例,它考虑使用两者(并且由于歧义而拒绝进一步)。 是什么让编译器的行为有所不同?
我的图书馆栏的用户将位于Bar::
名称空间之外,但我希望Bar::operator*
使用Foo::operator*
,而不是Bar::operator*(3.f,a)
。我考虑过明确地调用class AbstractEntity {
class func findOrCreateEntity() -> AnyObject? {
print("Name: \(NSStringFromClass(Mirror(reflecting: self)))")
}
}
,这是丑陋的,或者在全局命名空间中插入我自己的运算符,我认为这是Bad Thing。 我缺少一个选项,或者我做错了什么?
答案 0 :(得分:3)
在模板化的情况下,编译
f2
时,编译器会考虑使用Foo::operator*
而不是Bar::operator*
,而在非模板化的情况下,它会考虑使用两者(并且拒绝使用进一步因为含糊不清)。是什么让编译器的行为不同?
在这两种情况下,编译器都考虑使用两者,但在模板operator*
的情况下,调用不是模糊的,因为有一个非模板化的函数,参数类型与参数完全匹配(try replace {{ 1}} 3.f
,您将看到已找到模板化版本)。典型地:
3.
我的库Bar的用户将在
template <typename T> void g (T) { } void g (float) { } g(0.f); // Ok, the overload for float is preferred over the templated version
命名空间之外,但我希望使用Bar::
,而不是Foo :: operator *。我考虑过明确地调用Bar::operator*
,这是丑陋的,或者在全局命名空间中插入我自己的运算符,我认为这是一件坏事。有没有我想念的选项,或者我做错了什么?
不幸的是,ADL不会找到您的重载,因为Bar::operator*(3.f,a)
的唯一参数是operator*
和float
,它们是在命名空间MyDeriv
中定义的。一种可能的方法是继承Foo
:
Foo::Deriv
另一个是在namespace Bar {
struct MyDeriv: public Foo::Deriv {};
MyDeriv operator* (float x, const MyDeriv& d) { return MyDeriv();}
}
命名空间内声明operator*
的重载:
Foo