问题
I would like some light to be shed on a puzzling situation involving ADL, namespaces and operator overloading.
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();}
}
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();}
}
I observe a difference in compiler behaviour depending whether one is inside namespace Bar
or not.
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;
}
}
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
}
You can see the code live here :http://ideone.com/pkPeOY
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
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 ?A user of my library Bar will be outside the
Bar::
namespace, yet I wantBar::operator*
to be used, and notFoo::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 ?
回答1:
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 ?
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
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 ?
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();}
}
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(); }
}
来源:https://stackoverflow.com/questions/38329694/operator-overloading-name-resolution-and-namespaces