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

时间:2016-07-12 13:02:01

标签: c++ namespaces argument-dependent-lookup

我希望能够解决涉及ADL,名称空间和运算符重载的令人费解的情况。

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

namespace Foo {
    class Deriv {};
    class Another {};

    template <typename T>
    Another operator* ( T x, const Deriv& d ) { return Another();}
}

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

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

我发现编译器行为的差异取决于一个是否在namespace Bar内。

此函数(Bar::f1)使用operator *的第二个版本进行编译:

namespace Bar {
    void f1() {
        Bar::MyDeriv a;
        Bar::MyDeriv b = 3.f * a;
    }
} 

虽然命名空间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
}

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

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

所以,面对这种情况,这让我感到困惑

  • 模板的情况下,编译f2时,编译器会考虑使用Foo::operator*而不是Bar::operator*,而在非-templated 案例,它考虑使用两者(并且由于歧义而拒绝进一步)。 是什么让编译器的行为有所不同?

  • 我的图书馆栏的用户将位于Bar::名称空间之外,但我希望Bar::operator*使用Foo::operator*,而不是Bar::operator*(3.f,a)。我考虑过明确地调用class AbstractEntity { class func findOrCreateEntity() -> AnyObject? { print("Name: \(NSStringFromClass(Mirror(reflecting: self)))") } } ,这是丑陋的,或者在全局命名空间中插入我自己的运算符,我认为这是Bad Thing我缺少一个选项,或者我做错了什么?

1 个答案:

答案 0 :(得分:3)

  

在模板化的情况下,编译f2时,编译器会考虑使用Foo::operator*而不是Bar::operator*,而在非模板化的情况下,它会考虑使用两者(并且拒绝使用进一步因为含糊不清)。是什么让编译器的行为不同?

在这两种情况下,编译器都考虑使用两者,但在模板operator*的情况下,调用不是模糊的,因为有一个非模板化的函数,参数类型与参数完全匹配(try replace {{ 1}} 3.f,您将看到已找到模板化版本)。典型地:

3.
  

我的库Bar的用户将在template <typename T> void g (T) { } void g (float) { } g(0.f); // Ok, the overload for float is preferred over the templated version 命名空间之外,但我希望使用Bar::,而不是Foo :: operator *。我考虑过明确地调用Bar::operator*,这是丑陋的,或者在全局命名空间中插入我自己的运算符,我认为这是一件坏事。有没有我想念的选项,或者我做错了什么?

不幸的是,ADL不会找到您的重载,因为Bar::operator*(3.f,a)的唯一参数是operator*float,它们是在命名空间MyDeriv中定义的。一种可能的方法是继承Foo

Foo::Deriv

另一个是在namespace Bar { struct MyDeriv: public Foo::Deriv {}; MyDeriv operator* (float x, const MyDeriv& d) { return MyDeriv();} } 命名空间内声明operator*的重载:

Foo