簡體   English   中英

具有指針參數的函數模板重載解析

[英]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.

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