简体   繁体   English

运算符重载,名称解析和名称空间

[英]Operator overloading, name resolution and namespaces

I would like some light to be shed on a puzzling situation involving ADL, namespaces and operator overloading. 我想对涉及ADL,名称空间和运算符重载的令人费解的情况有所了解。

Let Foo be a library which defines a class ( Deriv ) in its own namespace, along with a templated operator * which returns another class. 让Foo成为一个库,它在自己的命名空间中定义一个类( Deriv ),以及一个返回另一个类的模板化operator *

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 . 现在我在我自己的库Bar中使用Foo的类,它定义了另一个operator * ,这次只用于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. 我发现编译器行为的不同取决于是否在namespace Bar

This function ( Bar::f1 ) compiles, using the second version of the operator * : 此函数( Bar::f1 )使用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* . 虽然命名空间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
}

You can see the code live here : http://ideone.com/pkPeOY 您可以在此处查看代码: http//ideone.com/pkPeOY

Now, if Foo::operator* was not templated and defined as Foo::operator*(float, const Deriv& d); 现在,如果Foo::operator*没有模板化并定义为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 然后这两个函数都无法使用相同的错误进行编译(模糊运算符重载),如下所示: 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). 模板化的情况下,编译f2 ,编译器会考虑使用Foo::operator*而不是Bar::operator* ,而在非模板化的情况下,它会考虑使用两者(并且由于模糊性而拒绝进一步使用)。 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* . 我的库Bar的用户将在Bar:: namespace之外,但我想要使用Bar::operator* ,而不是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 . 我考虑明确地打电话Bar::operator*(3.f,a)这是丑陋的,或插入我自己的全局命名空间中操作,这我认为是一个 事情 Is there an option I am missing, or am I doing something wrong ? 有没有我想念的选择,或者我做错了什么?

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). 在模板化的情况下,编译f2 ,编译器会考虑使用Foo::operator*而不是Bar::operator* ,而在非模板化的情况下,它会考虑使用两者(并且由于模糊性而拒绝进一步使用)。 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). 在这两种情况下,编译器都考虑使用两者,但在模板化operator*的情况下,调用不是模糊的,因为有一个非模板化函数,参数类型与参数完全匹配(尝试将3.f替换为3.和你会看到找到模板化的版本)。 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*. 我的库Bar的用户将在Bar:: namespace之外,但我想要使用Bar::operator* ,而不是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. 我考虑过明确地调用Bar::operator*(3.f,a) ,这是丑陋的,或者在全局命名空间中插入我自己的运算符,我认为这是一件坏事。 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 . 不幸的是,ADL不会找到你的重载,因为operator*的唯一参数是floatMyDeriv ,它们在命名空间Foo中定义。 One possible way would be to inherit from Foo::Deriv : 一种可能的方法是从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: 另一个是在Foo命名空间内为operator*声明重载:

namespace Bar {
    typedef Foo::Deriv MyDeriv;
}

namespace Foo {
    Bar::MyDeriv operator* (float x, const Bar::MyDeriv& d) { return Bar::MyDeriv(); }
}

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM