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 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 ?
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 ?
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(); }
}
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With