1

The following code compiles. Anyone can explain why? I've been digging the standard to figure out why it's legal.

template <bool B, typename T = void> struct enable_if { }; template <typename T> struct enable_if<true, T> { typedef T type; }; template <typename T, typename Enable = void> struct A; template <typename T> struct A<T, typename enable_if<(sizeof(T) <= ~0ULL)>::type> { void f() { } }; int main() { A<int> a; a.f(); } 

At the statement:

A<int> a; 

As there's only one template paramerter "int", the compiler should go to use the primary template, which is:

template <typename T, typename Enable = void> struct A; 

which is undefined, thus causing an error.

3
  • No the second template is the right one because a specialization as precedence over a more generic definition. The order is not significant here. You have two spaces : the generic templates and the specialized. If a specialized can be instanciated, it will, if not then a look will be given to the generics. Commented Apr 3, 2014 at 5:08
  • and why don't you just use std::enable_if? Commented Apr 3, 2014 at 5:15
  • @texasbruce a likely reason is that many people still needs to stick to c++98 Commented Apr 3, 2014 at 5:19

4 Answers 4

3

From § 14.5.5.1

1 When a class template is used in a context that requires an instantiation of the class, it is necessary to determine whether the instantiation is to be generated using the primary template or one of the partial specializations. This is done by matching the template arguments of the class template specialization with the template argument lists of the partial specializations.

— If exactly one matching specialization is found, the instantiation is generated from that specialization.

Sign up to request clarification or add additional context in comments.

Comments

1

Let's try to figure out what's going on here:

// definition of enable_if, second parameter is defaulted to void template <bool B, typename T = void> struct enable_if { }; // specialization of enable_if, if first parameter is true, // enable_if has a typedef for the second parameter template <typename T> struct enable_if<true, T> { typedef T type; }; // definition of struct A, second parameter defaults to void template <typename T, typename Enable = void> struct A; // specialization of struct A, second parameter // is obtained from the enable_if::type typedef // the first parameter of enable_if is true if the size of T // is smaller than the max long long (~0 --> all F) template <typename T> struct A<T, typename enable_if<(sizeof(T) <= ~0ULL)>::type> { void f() { } }; int main() { // So we attempt the specialization for struct A<int,enable_if...> // The expression of enable_if evaluates to... // (sizeof(int) <= ~0ULL) == true // ... so it applies the specialization of enable_if<true,void> // (second parameter is void because none is provided, so it // uses the default. // so the enable_if template is valid (selected the specialization) // and that means that the struct A<int,enable_if> specialization // is valid too, so it is selected. A<int> a; a.f(); } 

2 Comments

No it does not use the first enable_if because enable_if<true,void>::type cannot be deducted.
@texasbruce I meant that it uses the specialization of enable_if because the expression evaluates to true. I'll clarify
0

The compiler uses the template A<int, enable_if<true>:::type > when you declare A<int> since sizeof(int) <= ~0ULL evaluates to true.

There is no problem with enable_if<true>::type because the compiler is able to use enable_if<true, true>::type.

Comments

0

When you consider your enable_if:

template <bool B, typename T = void> struct enable_if{ }; template <typename T> struct enable_if<true, T> { typedef T type; }; 

in

void test_EnableIf { static_assert( std::is_same< enable_if<(sizeof(int) > 0)>::type, void>::value, "test_EnableIf failed." ); } 

the result (type) is void, as no type was specified (as second template parameter). The specialization of enable_if is selected because of the boolean expression being true, and the default parameter is selected (from primary template) because no other was provided, and hence type is void, but NOTE that the definition of type does exist (as the specialization was selected).

Now, in your definition of A...

template <typename T, typename Enable = void> struct A; template <typename T> struct A<T, typename enable_if< (sizeof(T) <= ~0ULL)>::type> { void f() { } }; 

...because type does exist in enable_if, it is a better match, which causes the specialization to be selected, and hence compiles.

A trivial example which amounts to the same thing is the following:

template <class T, class U = void> struct X; template <class T> struct X<T,void> { static int foo(){ return 0; } }; int main() { return X<int>::foo(); } 

Comments

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.