簡體   English   中英

運算符重載,名稱解析和名稱空間

[英]Operator overloading, name resolution and namespaces

我想對涉及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* ,而在非模板化的情況下,它會考慮使用兩者(並且由於模糊性而拒絕進一步使用)。 是什么讓編譯器的行為有所不同?

  • 我的庫Bar的用戶將在Bar:: namespace之外,但我想要使用Bar::operator* ,而不是Foo::operator* 我考慮明確地打電話Bar::operator*(3.f,a)這是丑陋的,或插入我自己的全局命名空間中操作,這我認為是一個 事情 有沒有我想念的選擇,或者我做錯了什么?

在模板化的情況下,編譯f2 ,編譯器會考慮使用Foo::operator*而不是Bar::operator* ,而在非模板化的情況下,它會考慮使用兩者(並且由於模糊性而拒絕進一步使用)。 是什么讓編譯器的行為有所不同?

在這兩種情況下,編譯器都考慮使用兩者,但在模板化operator*的情況下,調用不是模糊的,因為有一個非模板化函數,參數類型與參數完全匹配(嘗試將3.f替換為3.和你會看到找到模板化的版本)。 典型:

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:: namespace之外,但我想要使用Bar::operator* ,而不是Foo :: operator *。 我考慮過明確地調用Bar::operator*(3.f,a) ,這是丑陋的,或者在全局命名空間中插入我自己的運算符,我認為這是一件壞事。 有沒有我想念的選擇,或者我做錯了什么?

不幸的是,ADL不會找到你的重載,因為operator*的唯一參數是floatMyDeriv ,它們在命名空間Foo中定義。 一種可能的方法是從Foo::Deriv繼承:

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

另一個是在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