c++ - Template partial ordering - why does partial deduction succeed here -


consider following simple (to extent template questions ever are) example:

#include <iostream>  template <typename t> struct identity;  template <> struct identity<int> {     using type = int; };  template<typename t> void bar(t, t ) { std::cout << "a\n"; } template<typename t> void bar(t, typename identity<t>::type) { std::cout << "b\n"; }  int main () {     bar(0, 0); } 

both clang , gcc print "a" there. according rules in [temp.deduct.partial] , [temp.func.order], determine partial ordering, need synthesize unique types. have 2 attempts @ deduction:

+---+-------------------------------+-------------------------------------------+ |   | parameters                    | arguments                                 | +---+-------------------------------+-------------------------------------------+ | | t, typename identity<t>::type | uniquea, uniquea                          | | b | t, t                          | uniqueb, typename identity<uniqueb>::type | +---+-------------------------------+-------------------------------------------+ 

for deduction on "b", according richard corden's answer, expression typename identity<uniqueb>::type treated type , not evaluated. is, synthesized if were:

+---+-------------------------------+--------------------+ |   | parameters                    | arguments          | +---+-------------------------------+--------------------+ | | t, typename identity<t>::type | uniquea, uniquea   | | b | t, t                          | uniqueb, uniqueb_2 | +---+-------------------------------+--------------------+ 

it's clear deduction on "b" fails. 2 different types cannot deduce t both of them.

however, seems me deduction on a should fail. first argument, you'd match t == uniquea. second argument non-deduced context - wouldn't deduction succeed iff uniquea convertible identity<uniquea>::type? latter substitution failure, don't see how deduction succeed either.

how , why gcc , clang prefer "a" overload in scenario?

as discussed in comments, believe there several aspects of function template partial ordering algorithm unclear or not specified @ in standard, , shows in example.

to make things more interesting, msvc (i tested 12 , 14) rejects call ambiguous. don't think there's in standard conclusively prove compiler right, think might have clue difference comes from; there's note below.

your question (and this one) challenged me more investigation how things work. decided write answer not because consider authoritative, rather organize information have found in 1 place (it wouldn't fit in comments). hope useful.


first, proposed resolution issue 1391. discussed extensively in comments , chat. think that, while provide clarification, introduces issues. changes [14.8.2.4p4] (new text in bold):

each type nominated above parameter template , corresponding type argument template used types of p , a. if particular p contains no template-parameters participate in template argument deduction, p not used determine ordering.

not idea in opinion, several reasons:

  • if p non-dependent, doesn't contain template parameters @ all, doesn't contain participate in argument deduction either, make bold statement apply it. however, make template<class t> f(t, int) , template<class t, class u> f(t, u) unordered, doesn't make sense. arguably matter of interpretation of wording, cause confusion.
  • it messes notion of used determine ordering, affects [14.8.2.4p11]. makes template<class t> void f(t) , template<class t> void f(typename a<t>::a) unordered (deduction succeeds first second, because t not used in type used partial ordering according new rule, can remain without value). currently, compilers i've tested report second more specialized.
  • it make #2 more specialized #1 in following example:

    #include <iostream>  template<class t> struct { using = t; };  struct d { }; template<class t> struct b { b() = default; b(d) { } }; template<class t> struct c { c() = default; c(d) { } };  template<class t> void f(t, b<t>) { std::cout << "#1\n"; } // #1 template<class t> void f(t, c<typename a<t>::a>) { std::cout << "#2\n"; } // #2  int main() {    f<int>(1, d()); } 

    (#2's second parameter not used partial ordering, deduction succeeds #1 #2 not other way around). currently, call ambiguous, , should arguably remain so.


after looking @ clang's implementation of partial ordering algorithm, here's how think standard text changed reflect happens.

leave [p4] , add following between [p8] , [p9]:

for p / a pair:

  • if p non-dependent, deduction considered successful if , if p , a same type.
  • substitution of deduced template parameters non-deduced contexts appearing in p not performed , not affect outcome of deduction process.
  • if template argument values deduced template parameters of p except ones appear in non-deduced contexts, deduction considered successful (even if parameters used in p remain without value @ end of deduction process particular p / a pair).

notes:

  • about second bullet point: [14.8.2.5p1] talks finding template argument values that make p, after substitution of deduced values (call deduced a), compatible a. cause confusion happens during partial ordering; there's no substitution going on.
  • msvc doesn't seem implement third bullet point in cases. see next section details.
  • the second , third bullet points intented cover cases p has forms a<t, typename u::b>, aren't covered wording in issue 1391.

change current [p10] to:

function template f @ least specialized function template g if , if:

  • for each pair of types used determine ordering, type f @ least specialized type g, and,
  • when performing deduction using transformed f argument template , g parameter template, after deduction done pairs of types, template parameters used in types g used determine ordering have values, , values consistent across pairs of types.

f is more specialized g if f @ least specialized g , g not @ least specialized f.

make entire current [p11] note.

(the note added resolution of 1391 [14.8.2.5p4] needs adjusted - it's fine [14.8.2.1], not [14.8.2.4].)


for msvc, in cases, looks template parameters in p need receive values during deduction for specific p / a pair in order deduction succeed a p. think causes implementation divergence in example , others, i've seen @ least 1 case above doesn't seem apply, i'm not sure believe.

another example statement above seem apply: changing template<typename t> void bar(t, t) template<typename t, typename u> void bar(t, u) in example swaps results around: call ambiguous in clang , gcc, resolves b in msvc.

one example doesn't:

#include <iostream>  template<class t> struct { using = t; }; template<class, class> struct b { };  template<class t, class u> void f(b<u, t>) { std::cout << "#1\n"; } template<class t, class u> void f(b<u, typename a<t>::a>) { std::cout << "#2\n"; }  int main() {    f<int>(b<int, int>()); } 

this selects #2 in clang , gcc, expected, msvc rejects call ambiguous; no idea why.


the partial ordering algorithm described in standard speaks of synthesizing a unique type, value, or class template in order generate arguments. clang manages by... not synthesizing anything. uses original forms of dependent types (as declared) , matches them both ways. makes sense, substituting synthesized types doesn't add new information. can't change forms of a types, since there's no way tell concrete types substituted forms resolve to. synthesized types unknown, makes them pretty similar template parameters.

when encountering p non-deduced context, clang's template argument deduction algorithm skips it, returning "success" particular step. happens not during partial ordering, types of deductions, , not @ top level in function parameter list, recursively whenever non-deduced context encountered in form of compound type. reason, found surprising first time saw it. thinking it, does, of course, make sense, , according standard ([...] not participate in type deduction [...] in [14.8.2.5p4]).

this consistent richard corden's comments his answer, had see compiler code understand implications (not fault of answer, rather of own - programmer thinking in code , that).

i've included more information clang's implementation in this answer.


Comments

Popular posts from this blog

javascript - Using jquery append to add option values into a select element not working -

Android soft keyboard reverts to default keyboard on orientation change -

Rendering JButton to get the JCheckBox behavior in a JTable by using images does not update my table -