2013-02-20 29 views
12

考慮下面這個簡單的例子重載轉換運算模板

struct C 
{ 
    template <typename T> operator T() {return 0.5;} 
    operator int() {return 1;} 
    operator bool() {return false;} 
}; 

int main() 
{ 
    C c; 
    double x = c; 
    std::cout << x << std::endl; 
} 

當鏗鏘編譯,它提供了以下錯誤

test.cpp:11:12: error: conversion from 'C' to 'double' is ambiguous 
    double x = c; 
     ^ ~ 
test.cpp:4:5: note: candidate function 
    operator int() {return 1;} 
    ^
test.cpp:5:5: note: candidate function 
    operator bool() {return false;} 
    ^
test.cpp:3:27: note: candidate function [with T = double] 
    template <typename T> operator T() {return 0.5;} 
         ^
1 error generated. 

其他編譯器生成類似的錯誤,例如,GCC和英特爾ICLC

如果我刪除operator intoperator bool。它編譯得很好,並按預期工作。如果只刪除其中的一個,即保留模板運算符並說operator int,則始終選擇非模板版本。

我的理解是,只有當模板和非模板重載函數相同時,它們都是完全匹配或者兩者都需要相同的轉換序列,則非模板版本纔是首選。但是在這種情況下,編譯器似乎沒有看到運算符模板爲完美匹配。當存在boolint重載時,自然會認爲它們是不明確的。

總之,我的問題是,爲什麼運營商模板在這種情況下不被認爲是完美匹配?

回答

4

這很有趣。有兩種方法可以閱讀第13.3.3節的關鍵部分。原來的示例應該明確地調用函數模板,但其中一個非模板被刪除的版本可能會被認爲是不明確的。

13.3.3:

一個可行的函數F1被定義爲比另一個可行函數F2一個更好功能如果對所有參數,ICS_i(F1)不是惡化轉換序列比ICS_i(F2),然後

  • 對於一些參數Ĵ,ICS_j(F1)比ICS_j更好的轉換序列(F2),或者,如果不是,

  • 上下文是由用戶定義的轉換的初始化(見8.5,13.3.1.5和13.3.1.6 ),並且從返回類型F1到目標類型(即,正在初始化的實體的類型)的標準轉換序列是比從返回類型F2到目標類型的標準轉換序列更好的轉換序列,或者,如果不是,

  • F1是一個非模板函數和F2是一個函數模板專業化,或者,如果不說,

  • F1F2是功能模板特,而對於F1函數模板是比根據在14.5.6.2描述的部分排序規則的模板F2更專門的。

如果只有一個可行的函數比所有其他可行的函數更好,那麼它就是重載決策選擇的函數;否則該呼叫形式不良。

在該示例中,鐺正確地識別組的三個可行的候選的功能:

C::operator int() 
C::operator bool() 
C::operator double<double>() 

第三是一個函數模板特殊化。 (我不認爲上面的語法是合法的,但你有這樣的想法:在重載分辨率的這一點上,它不被視爲模板,而是作爲具有確定函數類型的專業化。)

唯一的隱式轉換此處的參數序列(ICS1)在隱式參數上完全匹配「左值C」到「C&」,因此不會產生差異。

這個例子正好是第二個項目符號所描述的情況,所以返回double的函數明顯比其他兩個更好。

下面是它變得奇怪的地方:由於第三個項目符號,通過非常直觀的閱讀,operator int也比模板專業化更好。 「等一下,不應該'比'好,反對稱嗎?你怎麼能說F1F2更好,F2F1好?」不幸的是,該標準沒有明確地說出任何這樣的事情。 「由於'如果不是那個'這個短語,第二個子彈不會優先於第三個子彈嗎?「是的,對於不斷F1F2,但標準不說,滿足第二子彈(F1,F2)使第三子彈(F2,F1)不適用。

當然,因爲operator int不超過operator bool,反之亦然更好,仍然存在「只有一個可行的功能,這是比所有其他可行功能更好的功能」

我並不完全贊同這個奇怪的閱讀,除了可能將其報告爲標準缺陷。後果(如刪除過載是而不是從這個例子中最好的改變一個程序從格式良好到曖昧!)。我認爲其意圖是在第三個項目符號被考慮之前,第二個項目符號被同時考慮。

這意味着函數模板應該通過重載決定來選擇,這是一個叮咚的bug。

+0

你對標準的「怪異閱讀」非常有趣,實際上對我來說意義重大。如果我理解正確,操作符int是否比模板更好取決於哪個是F1,哪個是F2。如果我們考慮F1 = op int,則F2 = op ,那麼在第3項我們確定F1比F2更好並停止。但是,如果我們考慮F1 = op ,F2 = op int,則在第2項我們確定F1更好並停止。但是如果它們有三個,那麼op int和op bool就不會比其他的更好,因此也就不會含糊不清 – 2013-02-21 00:37:29

6

讓我們將其分爲兩個不同的問題:

1.爲什麼這個編譯器錯誤?

struct C 
{ 
    operator bool() {return false;} 
    operator int() {return 1;} 
}; 

由於兩個intbool可以隱式轉換爲double,編譯器無法知道哪些功能它應該使用。有兩個函數可以使用,而且兩個函數都不會優先於另一個函數。

2.爲什麼模板版本不是完美匹配?

struct C 
{ 
    template <typename T> operator T() {return 0.5;} 
    operator int() {return 1;} 
}; 

請求時加倍爲什麼叫operator int()

非模板函數被調用是因爲非模板函數在重載解析中優先。 (Overloading function templates

編輯: 我錯了!正如周燕在他的評論中提到的那樣,正如我在提供的鏈接中所述,模板函數中的完美匹配優先於非模板函數。

我測試了你的代碼(用g ++ 4.7.2編譯),它按預期工作:它返回0.5,換句話說,模板函數被使用了!

編輯2: 我現在嘗試與叮噹,我可以重現你描述的行爲。由於它在gcc中正常工作,這似乎是叮噹聲中的一個錯誤。

+0

我已經在問題中指出了第一部分。然而,第二部分,其他方面相同時,非模板函數僅在重載分辨率方面優先。在這種情況下,模板不需要轉換,它返回一個double,而非模板運算符返回int或bool,然後需要轉換爲double。所以我仍然不明白爲什麼它不是一個完美的匹配。即使在你給的鏈接中,第二次調用f也表明完美匹配模板優先。非模板優先是第一個和第三個電話 – 2013-02-20 19:09:39

+0

@YanZhou你是對的,我更新了我的答案。但我的應用程序似乎有一個不同於你的行爲。我用gcc測試過,現在正在安裝clang進行比較。 – Misch 2013-02-20 19:20:05

+0

您使用的是什麼GCC版本?我試過G ++ 4.7,它給出了與Clang相同的錯誤,因此Intel icpc 13.1 – 2013-02-20 19:22:38