Home > Enterprise >  Resolution of class template with different default template argument and partial specialization arg
Resolution of class template with different default template argument and partial specialization arg

Time:11-22

I have two class (struct) templates A and B, which are identical except that the second parameter's default argument (in their primary templates) and the template-specializing second argument (in their partial specializations) are the same in A (both void), while different in B (void and int respectively).

#include <bits/stdc  .h>

/* primary class template A */
template <int N, class = void>
struct A : std::false_type {};

/* partial specialization of A */
template <int N>
struct A<N, std::enable_if_t<(N != 0), void>> : std::true_type {};

/* primary class template B */
template <int N, class = void>
struct B : std::false_type {};

/* partial specialization of B */
template <int N>
struct B<N, std::enable_if_t<(N != 0), int>> : std::true_type {};

int main() {

    std::cout << A<0>::value << std::endl; // 0 (i.e. A<0> extends std::false_type)
    std::cout << A<1>::value << std::endl; // 1 (i.e. A<1> extends std::true_type)

    std::cout << B<0>::value << std::endl; // 0 (i.e. B<0> extends std::false_type)
    std::cout << B<1>::value << std::endl; // 0 (i.e. B<1> extends std::false_type)

    return 0;
}

As is understood from the output, B<1> resolves to the primary template whereas A<1> resolves to the partial specialization, which I guess happens due to the aforementioned difference. This is rather counterintuitive as I expected the exact opposite to happen. But why does it happen? How does the compiler decide which version to resolve, particularly in this case?

Edit: As @Enlinco correctly identifies in his answer, my confusion was due to expecting that, when instantiating B<1>, the compiler would resolve the "more specialized for N != 0" version B<N, int>, preferring it to the "more generic" version B<N, void>.

CodePudding user response:

If I understand the confusion, when you see

/* primary class template B */
template <int N, class = void>
struct B : std::false_type {};

/* partial specialization of B */
template <int N>
struct B<N, std::enable_if_t<(N != 0), int>> : std::true_type {};

You think that when the compiler sees B<1> in main,

  • it sees that the general template is ok,
  • then it goes to see the specialization and
    • sees that (N != 0) is true,
    • resolves std::enable_if_t<(N != 0), int> to int
    • thus resulting in B<1, int>, which is good and to be preferred since it is a specialization.

The story is slightly different.

The line

template <int N, class = void>

just means, as suggested in a comment, that when you write B<an-int> the compiler sees B<an-int, void>.

If you look at it from this perspective, you should see why the mismatch causes the behavior you observe: B<1> is just B<1, void>, and no specialization is targeting that.

  • Related