运算符重载、名称解析和命名空间
Operator overloading, name resolution and namespaces
我想了解一些涉及 ADL、名称空间和运算符重载的令人费解的情况。
让 Foo 成为一个库,它在自己的命名空间中定义了一个 class(Deriv
),以及一个模板化的 operator *
,其中 returns 另一个 class.
namespace Foo {
class Deriv {};
class Another {};
template <typename T>
Another operator* ( T x, const Deriv& d ) { return Another();}
}
现在我在自己的库Bar中使用Foo的class,它定义了另一个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
所以,面对这种情况,这就是我很困惑的地方
在templated情况下,编译f2
时,编译器考虑使用Foo::operator*
而不是Bar::operator*
,而在 non-templated 的情况下,它考虑同时使用两者(并且由于含糊不清而拒绝更进一步)。 是什么让编译器的行为不同?
我的库 Bar 的用户将在 Bar::
命名空间之外,但我希望使用 Bar::operator*
,而不是 Foo::operator*
。我考虑过显式调用 Bar::operator*(3.f,a)
,这很丑陋,或者在全局命名空间中插入我自己的运算符,我认为这是一个 Bad Thing。 有没有我遗漏的选项,或者我做错了什么?
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). What makes the compiler behave differently ?
在这两种情况下,编译器都会考虑同时使用两者,但在模板化 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
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 ?
不幸的是,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(); }
}
我想了解一些涉及 ADL、名称空间和运算符重载的令人费解的情况。
让 Foo 成为一个库,它在自己的命名空间中定义了一个 class(Deriv
),以及一个模板化的 operator *
,其中 returns 另一个 class.
namespace Foo {
class Deriv {};
class Another {};
template <typename T>
Another operator* ( T x, const Deriv& d ) { return Another();}
}
现在我在自己的库Bar中使用Foo的class,它定义了另一个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
所以,面对这种情况,这就是我很困惑的地方
在templated情况下,编译
f2
时,编译器考虑使用Foo::operator*
而不是Bar::operator*
,而在 non-templated 的情况下,它考虑同时使用两者(并且由于含糊不清而拒绝更进一步)。 是什么让编译器的行为不同?我的库 Bar 的用户将在
Bar::
命名空间之外,但我希望使用Bar::operator*
,而不是Foo::operator*
。我考虑过显式调用Bar::operator*(3.f,a)
,这很丑陋,或者在全局命名空间中插入我自己的运算符,我认为这是一个 Bad Thing。 有没有我遗漏的选项,或者我做错了什么?
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.
,您将看到找到模板版本)。通常:
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*. 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*
的唯一参数是 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(); }
}