[英]Function template overload resolution with a pointer argument
以下代碼演示了我用來確定類型T
是否為特定類模板的實例化的C ++模板元編程模式的核心:
#include <iostream>
template<class A, class B>
struct S{};
template<class A, class B>
constexpr bool isS(const S<A,B>*) {return true;}
template<class T>
constexpr bool isS(const T*) {return false;}
int main() {
S<int,char> s;
std::cout<<isS(&s)<<std::endl;
return 0;
}
它具有constexpr
函數模板isS
兩個重載,並且按預期輸出1
。 如果我從第二個isS
刪除指針,即用
template<class T>
constexpr bool isS(const T) {return false;}
程序意外輸出0
。 如果isS
兩個版本都isS
編譯的重載解析階段,則輸出表明編譯器正在選擇第二個重載。 我已經使用此處的在線編譯器在GCC,Clang和vc ++下對此進行了測試,它們均產生相同的結果。 為什么會這樣?
我已經多次閱讀Herb Sutter的“為什么不專門使用功能模板”一文,看來這兩個isS
函數都應被視為基本模板。 如果是這樣,那么問題是哪個是最專業的。 根據直覺和這個答案 ,我希望第一個isS
是最專業的,因為T
可以匹配S<A,B>*
每個實例,並且T
許多可能實例不能匹配S<A,B>*
。 我想在工作草案中找到定義此行為的段落,但是我不確定是哪個編譯階段引起了問題。 與“ 14.8.2.4在部分排序期間推導模板參數”有關嗎?
鑒於以下代碼(其中第一個isS
引用const S<A,B>
,第二個采用const T
)輸出期望值1
,因此,此問題尤其令人驚訝:
#include <iostream>
template<class A, class B>
struct S{};
template<class A, class B>
constexpr bool isS(const S<A,B>&) {return true;}
template<class T>
constexpr bool isS(const T) {return false;}
int main() {
S<int,char> s;
std::cout<<isS(s)<<std::endl;
return 0;
}
因此,問題似乎與如何處理指針有關。
因為第二個重載會將頂級const
放入const T
,所以它將在推論參數時解析為T*
。 第一次重載是更糟糕的匹配,因為它將解析為S<int, char> const*
,這需要進行const限定轉換。
您需要在變量s
前面添加const
,以便啟動更專業的重載:
#include <iostream>
template<class A, class B>
struct S {};
template<class A, class B>
constexpr bool isS(const S<A,B>*) {return true;}
//template<class T>
//constexpr bool isS(const T*) {return false;}
template<class T>
constexpr bool isS(const T) {return false;}
int main() {
S<int,char> const s{}; // add const here
std::cout<<isS(&s)<<std::endl;
return 0;
}
將第一個重載更改為const S<A,B>&
將產生正確的結果,因為存在身份轉換而不是資格調整。
13.3.3.1.4參考綁定[over.ics.ref]
1當引用類型的參數直接(8.5.3)綁定到參數表達式時,隱式轉換序列是恆等轉換,除非參數表達式的類型是參數類型的派生類,在這種情況下隱式轉換序列是從派生基數轉換(13.3.3.1)。
注意 :如果對這樣的參數推導游戲有疑問,可以使用__PRETTY_FUNCTION__
宏,該宏(在gcc / clang上)將為您提供有關所選模板的推導類型的更多信息。 然后,您可以注釋掉某些重載,以了解這如何影響重載分辨率。 請參見此示例 。
您的第二個版本沒有提供您期望的答案,因為isS
的第一個版本需要隱式轉換,而第二個版本則不需要。
template<class A, class B>
constexpr bool isS(const S<A,B>*);
template<class T>
constexpr bool isS(const T);
S<int,char> s;
isS(&s);
請注意, &s
的類型為S<int,char>*
。 第一個isS
正在尋找const S<int,char>*
,因此指針需要轉換。 第二個isS
是直接匹配。
如果您發現自己經常需要這種模式,則可以將其概括化,如下所示:
template<template<typename...> class TT, typename T>
struct is_specialization_of : std::false_type {};
template<template<typename...> class TT, typename... Ts>
struct is_specialization_of<TT, TT<Ts...>> : std::true_type {};
然后檢查類型是否是S
例如:
is_specialization_of<S, decltype(s)>::value
聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.