C++ Notes - SFINAE & std::enable_if
SFINAE
SFINAE is the abbreviation of “substitution failure is not an error“, that is, matching failure is not an error. That is to say, if matching an overloaded function/class will cause a compilation error, the function/class will not be a candidate and the compiler will look for another overload version. This is a new feature of C++11 and the core principle of enable_if.
Complete Overload Matching Order
- Find all candidates and remove those who will cause compile error.
- Try to find a viable version:
- Exact argument type matching is possible.
- value -> reference
- array -> pointer
- function -> function pointer
- pointer -> const pointer
- pointer -> volatile pointer
- Argument types can be matched through default parameters.
- Argument types can be matched through default type conversions.
- char/short -> int/float/double
- int -> char
- long -> double
- etc.
- Argument types can be matched through user-defined type conversions.
- class constructors
- type conversion functions
- etc.
- Exact argument type matching is possible.
- Non-template functions take precedence over template functions.
- Matching failed
- The final found feasible functions are not unique, resulting in ambiguity and compilation failure.
- Unable to match all candidates, the function is undefined, resulting in compilation failure.
Example
1 |
|
The code provided appears to have an issue: it seems to use the returnValue type defined in testB when we haven’t explicitly specified the argument type as either testA or testB. It might seem like passing a testA type would cause a compilation error.
However, thanks to the SFINAE mechanism, the compiler treats both add functions as candidates during type deduction. When it encounters a failure (for example, when the input type is testA, but returnValue is not defined in it), the compiler discards that template and proceeds to other functions without producing an error.
std::enable_if
Principle
The definition of std::enable_if is pretty simple:
1 |
|
The former is a regular version of a template class, and the latter is a partial specialization version of a template class.
In that case, std::enable_if<true, T>::type would be T, and &std::enable_if<false, T>::type would cause a compile error. (under SFINAE, the compiler will not consider functions/classes containing this kind of enable_if as a candidate)
1 |
|
Usage
std::enable_if could be used anywhere as a typename. It is pretty useful in C++ template meta progamming.
- Template Partial Specialization
1 |
|
When you call check<some typename>(), compiler will generate functions called void check() and output “T is (not) trivial” to the console depending on what you put in <>.
- Validate Template’s Parameter Types
1 |
|
In the example above, we are validating the template’s parameter type by two ways: defining the return value and the default template parameter. By defining function like this, only intergral type can be used to call is_odd and is_even.
std::enable_if_t
It is easy to understand std::enable_if_t with std::enable_t. Its definition is:
1 |
|
It is simply the alias of std::enable_if<_Test, _Ty>::type. You can use this as a typename directly in the code
1 |
|