c++ - Function template overload resolution with a pointer argument -


the following code demonstrates core of c++ template metaprogramming pattern have been using determine whether type t instantiation of specific class template:

#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; } 

it features 2 overloads of constexpr function template iss, , outputs 1, expected. if remove pointer second iss, i.e. replace with

template<class t> constexpr bool iss(const t) {return false;} 

the program unexpectedly outputs 0. if both versions of iss make through overload resolution phase of compilation, output implies compiler choosing second overload. have tested under gcc, clang , vc++ using online compilers here, , produce same result. why happen?

i have read herb sutter's "why not specialize function templates" article several times, , seems both iss functions should considered base templates. if so, question of 1 specialised. going intuition , this answer, expect first iss specialised, because t can match every instantiation of s<a,b>*, , there many possible instantiations of t cannot match s<a,b>*. i'd locate paragraph in working draft defines behaviour, i'm not entirely sure phase of compilation causing problem. "14.8.2.4 deducing template arguments during partial ordering"?

this issue particularly surprising given following code, in first iss takes reference const s<a,b> , second takes const t, outputs expected value 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; } 

so problem seems how pointers treated.

because second overload drop top-level const inside const t, resolve t* during argument deduction. first overload worse match because resolve s<int, char> const*, requires const-qualification conversion.

you need add const in front of variable s in order more specialized overload kick in:

#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; } 

live example

changing first overload const s<a,b>&, give correct result because there identity conversion instead of qualification adjustment.

13.3.3.1.4 reference binding [over.ics.ref]

1 when parameter of reference type binds directly (8.5.3) argument expression, implicit conversion sequence identity conversion, unless argument expression has type derived class of parameter type, in case implicit conversion sequence derived-to-base conversion (13.3.3.1).

note: when in doubt such argument deduction games, it's handy use __pretty_function__ macro (on gcc/clang) give more information deduced types of selected template. can comment out overloads see how affects overload resolution. see live example.


Comments