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)istrue, - resolves
std::enable_if_t<(N != 0), int>toint - thus resulting in
B<1, int>, which is good and to be preferred since it is a specialization.
- sees that
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.
