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.