C/C++ Arena

SFINAE and std::enable_if explained

What "substitution failure is not an error" means, how std::enable_if and std::void_t switch template overloads on and off, and how C++20 concepts replace them.

When the compiler tries a function template during overload resolution, it substitutes the deduced types into the template's declaration. If that produces something invalid, it doesn't report an error: it quietly drops that candidate and tries the others. That rule is SFINAE, "substitution failure is not an error", and before C++20 it was the standard way to constrain templates.

In C++20, concepts say the same thing directly (template <std::integral T>, requires), with far clearer error messages. Write concepts in new code, and recognize enable_if and void_t in the many libraries written before them.

Example

#include <iostream>
#include <string>
#include <type_traits>
#include <utility>
#include <vector>

template <typename T>
std::enable_if_t<std::is_integral_v<T>, std::string> kind(T) { return "integer"; }

template <typename T>
std::enable_if_t<std::is_floating_point_v<T>, std::string> kind(T) { return "floating point"; }

// Detection idiom: does T have a size() member?
template <typename T, typename = void>
struct has_size : std::false_type {};
template <typename T>
struct has_size<T, std::void_t<decltype(std::declval<const T&>().size())>> : std::true_type {};

int main() {
    std::cout << kind(7) << ", " << kind(0.5) << ", " << kind('x') << "\n";
    std::cout << std::boolalpha << has_size<std::vector<int>>::value << " " << has_size<double>::value << "\n";
    return 0;
}

Output:

integer, floating point, integer
true false

Practice it