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; }
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
Post a Comment