簡體   English   中英

為什么這個涉及重載運算符和隱式轉換的C ++表達式不明確?

[英]Why is this C++ expression involving overloaded operators and implicit conversions ambiguous?

在下面的例子中, operator bool打破了operator<的使用。 任何人都可以解釋為什么boolif (a < 0)表達式中的特定運算符相關,是否有解決方法?

struct Foo {
    Foo() {}
    Foo(int x) {}

    operator bool() const { return false; }

    friend bool operator<(const Foo& a, const Foo& b) {
        return true;
    }
};

int main() {
    Foo a, b;
    if (a < 0) {
        a = 0;
    }
    return 1;
}

當我編譯時,我得到:

g++ foo.cpp
foo.cpp: In function 'int main()':
foo.cpp:18:11: error: ambiguous overload for 'operator<' (operand types are 'Foo' and 'int')
     if (a < 0) {
           ^
foo.cpp:18:11: note: candidate: operator<(int, int) <built-in>
foo.cpp:8:17: note: candidate: bool operator<(const Foo&, const Foo&)
     friend bool operator<(const Foo& a, const Foo& b)

這里的問題是C ++有兩個選項來處理a < 0表達式:

  • a轉換為bool ,並使用內置運算符< (一次轉換)將結果與0進行比較
  • 0轉換為Foo ,並將結果與<您定義的結果(一次轉換)進行比較

這兩種方法都等同於編譯器,因此它會發出錯誤。

您可以通過在第二種情況下刪除轉換來明確這一點:

if (a < Foo(0)) {
    ...
}

重點是:

首先, operator <有兩個相關的重載。

  • operator <(const Foo&, const Foo&) 使用此重載需要使用Foo(int)將文字0轉換為Foo的用戶定義。
  • operator <(int, int) 使用此重載需要使用用戶定義的operator bool()Foo轉換為bool ,然后將提升轉換int (在標准中,這與轉換不同,正如Bo Persson所指出的那樣)。

這里的問題是:模糊性從何而來? 當然,第一次呼叫只需要用戶定義的轉換,比第二次呼叫更明智,這需要用戶定義的轉換,然后是促銷?

但事實並非如此。 該標准為每個候選人分配一個等級。 但是,“用戶定義的轉換后跟促銷”沒有等級。 這與僅使用用戶定義的轉換具有相同的等級。 簡單地(但非正式地)放置,排名順序看起來有點像這樣:

  1. 完全符合
  2. (僅)促銷要求
  3. (僅)需要隱式轉換(包括從C繼承的“不安全”轉換,如floatint
  4. 需要用戶定義的轉換

(免責聲明:如上所述,這是非正式的。當涉及多個參數時,它會變得更加復雜,而且我也沒有提到參考或cv資格。這只是作為一個粗略的概述。)

所以,希望這可以解釋為什么這個電話是模棱兩可的。 現在就如何解決這個問題的實際部分。 幾乎從來沒有提供operator bool()希望它隱含地用在涉及整數運算或比較的表達式中。 在C ++ 98中,有一些模糊的解決方法,從std::basic_ios<CharT, Traits>::operator void *到“改進的”更安全版本,涉及指向成員或不完整私有類型的指針。 幸運的是,在隱式使用operator bool()之后,C ++ 11引入了一種更可讀且更一致的方法來阻止整數提升,這是為了將運算符標記為explicit 這將完全刪除operator <(int, int)重載,而不是僅僅“降級”它。

正如其他人所提到的,您也可以將Foo(int)構造函數標記為顯式。 這將消除operator <(const Foo&, const Foo&)重載的相反效果。

第三種解決方案是提供額外的重載,例如:

  • operator <(int, const Foo&)
  • operator <(const Foo&, int)

在這個例子中,后者將優先於上述重載作為完全匹配,即使你沒有explicit引入。 例如,同樣如此

  • operator <(const Foo&, long long)

這比a < 0 operator <(const Foo&, const Foo&)因為它的使用僅需要促銷。

因為編譯器不能在bool operator <(const Foo &,const Foo &)operator<(bool, int) ,它們都適合這種情況。

為了解決這個問題,請使第二個構造函數explicit

struct Foo
{
    Foo() {}
    explicit Foo(int x) {}

    operator bool() const { return false; }

    friend bool operator<(const Foo& a, const Foo& b)
    {
        return true;
    }
};

編輯:好的,最后我得到了一個問題的真正意義:) OP問為什么他的編譯器提供operator<(int, int)作為候選者,盡管“不允許多步轉換”

答:是的,為了調用operator<(int, int)對象a需要將其轉換為Foo -> bool -> int 但是 ,C ++標准實際上並沒有說“多步轉換是非法的”。

§12.3.4[class.conv]

最多一個用戶定義的轉換(構造函數或轉換函數)隱式應用於單個值。

bool to int 不是用戶定義的轉換,因此它是合法的,編譯器有權選擇operator<(int, int)作為候選者。

這正是編譯器告訴你的。

解決編譯器的if (a < 0)一種方法是使用您提供的Foo(int x)構造函數從0創建對象。

第二個是使用operator bool轉換並將其與int (提升)進行比較。 您可以在數字促銷部分閱讀更多相關信息。

因此,它對於編譯器來說是模棱兩可的,它無法決定你希望它走哪條路。

暫無
暫無

聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM