Operator overloading, name resolution and namespaces

流过昼夜 提交于 2019-12-10 19:53:09

问题


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 ?


回答1:


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 ?

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 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 ?

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

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!