运算符重载,名称解析和名称空间 [英] Operator overloading, name resolution and namespaces

查看:122
本文介绍了运算符重载,名称解析和名称空间的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

我希望对涉及ADL,名称空间和运算符重载的令人困惑的情况有所了解.

I would like some light to be shed on a puzzling situation involving ADL, namespaces and operator overloading.

让Foo成为一个在其自己的名称空间中定义一个类(Deriv)以及返回另一个类的模板化operator *的库.

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();}
}

现在,我在自己的库Bar中使用Foo的类,该库定义了另一个operator *,这次仅用于float.

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();}
}

我观察到编译器行为的不同,具体取决于是否在namespace Bar内部.

I observe a difference in compiler behaviour depending whether one is inside namespace Bar or not.

此功能(Bar::f1)使用operator *的第二版进行编译:

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;
    }
} 

,但名称空间栏(f2())外部的同一函数无法编译,因为编译器仅尝试使用Foo::operator*,并且无法猜测它必须使用Bar::operator*.

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
}

您可以在此处查看代码: http://ideone.com/pkPeOY

You can see the code live here :http://ideone.com/pkPeOY

现在,如果未将Foo::operator*模板化并定义为Foo::operator*(float, const Deriv& d);,则两个函数均无法编译,并出现相同错误(歧义运算符重载),如此处所示: http://ideone.com/wi1EWS

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

  • 模板化情况下,编译f2时,编译器会考虑使用Foo::operator*而不是Bar::operator*,而在非模板化中在这种情况下,它会考虑同时使用两者(并且由于模棱两可而拒绝走得更远). 是什么使编译器的行为有所不同?

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

我的图书馆Bar的用户将位于Bar::命名空间之外,但我希望使用Bar::operator*,而不是Foo::operator*.我考虑过显式地调用丑陋的Bar::operator*(3.f,a),或在全局名称空间中插入自己的运算符,我认为这是事物. 我是否缺少选项,或者我做错了什么?

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 ?

推荐答案

在有模板的情况下,编译f2时,编译器考虑使用Foo::operator*而不是Bar::operator*,而在没有模板的情况下,它考虑同时使用(并且由于歧义而拒绝继续使用) .是什么使编译器的行为有所不同?

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 ?

在这两种情况下,编译器都考虑同时使用这两种方法,但是在使用模板化的operator*的情况下,该调用不会产生歧义,因为存在一个非模板化的函数,该函数的参数类型与参数完全匹配(请尝试用3.f替换3.f 3.,您将看到找到模板版本).通常:

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

我的图书馆Bar的用户将位于Bar::命名空间之外,但我希望使用Bar::operator*,而不是Foo :: operator *.我考虑过显式地调用丑陋的Bar::operator*(3.f,a),或在全局名称空间中插入自己的运算符,我认为这是一件坏事.是否有我遗漏的选项,或者我做错了什么?

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 ?

不幸的是,ADL找不到您的重载,因为operator*的唯一参数是在名称空间Foo中定义的floatMyDeriv.一种可能的方法是从Foo::Deriv继承:

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();}
}

另一种方法是在Foo名称空间内声明operator*的重载:

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(); }
}

这篇关于运算符重载,名称解析和名称空间的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆