简体   繁体   中英

How does enable_if help select specializations of a class template?

I have a basic grasp of SFINAE, and I think I understand many of the examples of how std::enable_if exploits it to select function template specializations, but I'm having a hard time wrapping my head around how it works for class templates.

The following example is from cppreference.com's explanation of std::enable_if :

template<class T, class Enable = void>
class A {}; // primary template

template<class T>
class A<T, typename std::enable_if<std::is_floating_point<T>::value>::type> {
}; // specialization for floating point types

I'm having trouble understanding how using std::enable_if in this way helps select the specialization. (I don't doubt that it does.)

When the compiler sees a declaration like A<float> specialized; , it will see two possible template instantiations that fit:

  1. The "primary template" A<T, Enable> where T is the type float and Enable is the type void (because of the default value).
  2. The specialization A<T, void> where T is the type float and void is the result of the expression with the enable_if .

Aren't those ambiguous? Both effectively result in A<T, void> , so why is the specialization chosen?

In a different case, like A<int> primary; , the options to the compiler seem to be:

  1. The primary, A<T, Enable> , where T is the type int and Enable is the type void .
  2. The specialization, A<T, ?> , where T is the type int and the ? represents where I'm completely lost. In this case, the enable_if condition is false, so it doesn't define type , which leaves you with A<int, typename > . Isn't that a syntax error? Even in the face of SFINAE?

From the reference on partial specialization of class templates:

When a class or variable (since C++14) template is instantiated, and there are partial specializations available, the compiler has to decide if the primary template is going to be used or one of its partial specializations.

If only one specialization matches the template arguments, that specialization is used

In this case, if the 2nd argument of the specialization is well-formed, it is chosen, precisely because it is a specialization, and not the primary template.

In case the 2nd template argument is not well-formed, then SFINAE kicks in. In particular:

When substituting the explicitly specified or deduced type for the template parameter fails, the specialization is discarded from the overload set instead of causing a compile error .

and

The following type errors are SFINAE errors:

attempting to use a member of a type, where the type does not contain the specified member

How this is done, ie how exactly the compiler discards the specialization, instead of giving an error, is not specified; the compiler is just required to do the right thing.

Aren't those ambiguous? Both effectively result in A<T, void> , so why is the specialization chosen?

No, the specialization is more specialized than the primary template, because it requires the second parameter to be void (assuming the enable_if condition is true), while the primary template doesn't restrict it.

The specialization, A<T, ?> , where T is the type int and the ? represents where I'm completely lost. In this case, the enable_if condition is false, so it doesn't define type, which leaves you with A<int, typename > . Isn't that a syntax error? Even in the face of SFINAE?

Exactly, the second argument in the specialization turns out invalid. But this is a "soft" error, which is detected by SFINAE and makes the compiler discard the specialization. (I don't think a compiler would textually replace enable_if_t<...>::type with an empty string and then analyze A<int, typename > ; more likely it would discard the specialization as soon as it notices the lack of ::type in enable_if .)

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM