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 particularp
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, maketemplate<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, becauset
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 , ifp
,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 inp
remain without value @ end of deduction process particularp
/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 deduceda
), compatiblea
. 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 formsa<t, typename u::b>
, aren't covered wording in issue 1391.
change current [p10] to:
function template
f
@ least specialized function templateg
if , if:
- for each pair of types used determine ordering, type
f
@ least specialized typeg
, and,- when performing deduction using transformed
f
argument template ,g
parameter template, after deduction done pairs of types, template parameters used in typesg
used determine ordering have values, , values consistent across pairs of types.
f
is more specializedg
iff
@ least specializedg
,g
not @ least specializedf
.
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
Post a Comment