[英]Overloading conversion operator template
考慮以下簡單示例
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;
}
使用 Clang 編譯時,會出現以下錯誤
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 和 Intel iclc
如果我刪除operator int
和operator bool
。 它編譯良好並按預期工作。 如果只刪除其中之一,即保留模板運算符並說operator int
,則始終選擇非模板版本。
我的理解是,只有當模板和非模板重載函數在兩者完美匹配或兩者都需要相同轉換序列的意義上相等時,才會首選非模板版本。 但是,在這種情況下,編譯器似乎沒有將運算符模板視為完美匹配。 當bool
和int
重載都存在時,它自然會認為它們是不明確的。
總之,我的問題是為什么在這種情況下運算符模板不被認為是完美匹配?
讓我們將其分解為兩個不同的問題:
1. 為什么會產生編譯錯誤?
struct C
{
operator bool () {return false;}
operator int () {return 1;}
};
由於int
和bool
都可以隱式轉換為double
,編譯器無法知道它應該使用哪個函數。 它可以使用兩個函數,並且沒有一個優先於另一個。
2. 為什么模板化版本不完美匹配?
struct C
{
template <typename T> operator T () {return 0.5;}
operator int () {return 1;}
};
為什么在請求 double 時調用operator int()
?
調用非模板函數是因為非模板函數在重載解析中具有優先權。 ( 重載函數模板)
編輯:我錯了! 正如 Yan Zhou 在他的評論中提到的,正如我在提供的鏈接中所述,模板化函數中的完美匹配優先於非模板化函數。
我測試了你的代碼(用 g++ 4.7.2 編譯),它按預期工作:它返回0.5
,換句話說,使用了模板函數!
EDIT2:我現在嘗試使用 clang 並且可以重現您描述的行為。 由於它在 gcc 中正常工作,這似乎是 clang 中的一個錯誤。
這很有趣。 有兩種方法可以閱讀第 13.3.3 節的關鍵部分。 原始示例肯定應該調用函數模板,但是刪除非模板之一的版本可能會被認為是模棱兩可的。
13.3.3:
如果對於所有參數i ICS_i(
F1
) 不是比 ICS_i(F2
) 更差的轉換序列,那么一個可行函數F1
被定義為比另一個可行函數F2
更好的函數,然后
對於某些參數j , ICS_j(
F1
) 是比 ICS_j(F2
) 更好的轉換序列,或者,如果不是,上下文是通過用戶定義轉換的初始化(見 8.5、13.3.1.5 和 13.3.1.6),從
F1
的返回類型到目標類型(即被初始化的實體的類型)的標准轉換序列是一個比從F2
的返回類型到目標類型的標准轉換序列更好的轉換序列,或者,如果不是,
F1
是一個非模板函數,F2
是一個函數模板特化,或者,如果不是,
F1
和F2
是函數模板特化,根據 14.5.6.2 中描述的偏序規則,F1
的函數模板比F2
的模板更特化。如果恰好有一個可行函數比所有其他可行函數更好,那么它就是通過重載決議選擇的那個; 否則調用格式錯誤。
在示例中,clang 正確識別了三個可行的候選函數集:
C::operator int()
C::operator bool()
C::operator double<double>()
第三個是函數模板特化。 (我不認為上面的語法是合法的,但你明白了:在重載解析的這一點上,它不被視為模板,而是作為具有明確函數類型的特化。)
此處參數的唯一隱式轉換序列 (ICS1) 是隱式參數上的“lvalue C
”到“ C&
”的精確匹配,因此不會產生任何影響。
這個例子正是第二個要點中描述的情況,所以返回double
的函數顯然比其他兩個好。
這就是奇怪的地方:從字面上看, operator int
也比模板特化更好,因為第三個要點。 “等一下,不應該'優於'反對稱嗎?你怎么能說F1
比F2
, F2
比F1
?” 不幸的是,標准沒有明確說明任何此類內容。 “不是因為'如果不是那個'短語,第二個子彈優先於第三個子彈嗎?” 是的,對於常數F1
和F2
。 但是標准並沒有說滿足(F1,F2)
的第二個項目符號會使(F2,F1)
的第三個項目符號不適用。
當然,由於operator int
並不比operator bool
,反之亦然,因此仍然“只有一個可行的函數比所有其他可行的函數都更好”。
我並不完全贊同這種奇怪的閱讀,除了可能將其報告為標准缺陷。 這樣做會產生奇怪的后果(比如從這個例子中刪除不是最好的重載將程序從格式良好變為模棱兩可!)。 我認為目的是在考慮第三個子彈之前,以兩種方式考慮第二個子彈。
這意味着應該通過重載決議來選擇函數模板,這是一個clang錯誤。
聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.