Role of default template arguments in the context of partial specialization
Asked Answered
A

1

0

I am not clear about the interaction of default template arguments in the context of partial specialization, for choosing which is the better matching template. This questions stems from code posted in this answer by max66.

Given the definitions of the classes A and B:

template <int N> struct A { static const int code = N; };

struct B{};

and the following template classes:

// primary template
template <typename, typename Enable = bool_constant<true>>
struct cond : public bool_constant<false> {};

// specialization
template <typename T>
struct cond<T, bool_constant<(0 == T::code)>> : public bool_constant<true> {};

1) cond<B>::value evaluates to false (i.e. the primary is chosen). This is clear as the primary template yields cond<B, bool_constant<true>>, the specialization fails, hence the primary template is the only possible choice.

2) cond<A<0>>::value evaluates to true (i.e. the specialization is chosen). This is clear as the primary template yields cond<B, bool_constant<true>>, the specialization also yields cond<B, bool_constant<true>>, hence the specialization is preferred because the argument for the 2nd template parameter is explicitly given.

3) cond<A<1>>::value evaluates to false (i.e. the primary is chosen). This is not clear to me. The primary template yields cond<B, bool_constant<true>>, the specialization yields cond<B, bool_constant<false>>. Given the argument for the 2nd template parameter is explicitly given in the specialization, why is not preferred?

I suppose the behaviour in (3) is due to some interaction between the default template argument of the primary template and the specialization. In this answer Jerry Coffin states something which might explain this behaviour:

if we change the specialization so that its specialization is for a type other than the default provided by the base template then the base template will be chosen.

Can somebody please elaborate on this rule? Thanks

Auditor answered 1/10, 2018 at 2:16 Comment(0)
F
1
template <typename, typename Enable = bool_constant<true>>
struct cond : public bool_constant<false> {};

is identical to

template <typename, typename Enable = bool_constant<true>> struct cond;

template <typename, typename Enable>
struct cond : public bool_constant<false> {};

Later might be clearer to understand the result.

When you write cond<C>, thanks to default argument, it is equivalent to cond<C, bool_constant<true>>.

Then we try to match that to "better instantiation".

We have the choice between:

// primary template
template <typename, typename Enable>
struct cond : public bool_constant<false> {};

and partial specialization, which use SFINAE:

// specialization
template <typename T>
struct cond<T, bool_constant<(0 == T::code)>> : public bool_constant<true> {};

If 0 == T::code is ill formed, specialization is discarded and only primary template is a viable solution, so it is used.

Else if 0 == T::code evaluates to false, the specialization doesn't match and primary template is also used.
Note that using cond<C, bool_constant<false>> would use the specialization in that case.

Else, 0 == T::code evaluates to true, and then both primary and specialization are viable, but specialization is more specialized, so it is chosen.

Ferule answered 1/10, 2018 at 8:17 Comment(6)
Could you please elaborate on the case where 0 == T::code evaluates to false? Why it does not match? Why using explicitly cond<C, bool_constant<false>> would instead match?Auditor
As I said, cond<C> is cond<C, bool_constant<true>> and cond<C, bool_constant<true>> doesn't match cond<T, bool_constant<false>> (T == C but true != false).Ferule
I am still confused. I can see it does not match. The question is, why should it match to be a valid specialization? My understanding of default template arguments is that, if one is not specifically provided, then the default is used. But if any valid type is explicitly provided, be it bool_constant<false> or void or whatever, that should succesfully replace the default value and therefore be preferred. Here we are saying that this will only happen if that matches the default. What is the precise rule?Auditor
First we fill missing arguments by default. so cond<C> -> cond<C, bool_constant<true>>. For the definition of primary template, it is a catch all, so any cond<T, U> can use that definition, but it is not necessary the more specialized. The SFINAE version is more specialized, so once correctly formed, it take precedence over the primary template (if it matches).Ferule
Ok, the last part of the last sentence is the answer I was looking for: "The SFINAE version is more specialized, so once correctly formed, it take precedence over the primary template (if it matches)". You may consider emphasizing this in your answer, as this was the culprit. Any reference I can read?Auditor
See partial_specialization. (Especially "Partial ordering").Ferule

© 2022 - 2024 — McMap. All rights reserved.