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(); }
}
The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.