[英]Why is this C++ expression involving overloaded operators and implicit conversions ambiguous?
在下面的例子中, operator bool
打破了operator<
的使用。 任何人都可以解釋為什么bool
與if (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所指出的那樣)。 這里的問題是:模糊性從何而來? 當然,第一次呼叫只需要用戶定義的轉換,比第二次呼叫更明智,這需要用戶定義的轉換,然后是促銷?
但事實並非如此。 該標准為每個候選人分配一個等級。 但是,“用戶定義的轉換后跟促銷”沒有等級。 這與僅使用用戶定義的轉換具有相同的等級。 簡單地(但非正式地)放置,排名順序看起來有點像這樣:
float
到int
) (免責聲明:如上所述,這是非正式的。當涉及多個參數時,它會變得更加復雜,而且我也沒有提到參考或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.