Skip to content

Substitution Failure Is Not An Error

Substitution Failure Is Not An Error

This rule applies during overload resolution of function templates: 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.

Because the attempted substitution is not an error, SFINAE is used as alternative to concepts in compilers that do not support C++20. The technique specifies the template parameters is such a way that only the intended template types are instanciated.

1
#include <type_traits>

1
2
3
4
5
6
7
8
9
// The overload fails if the first parameter is not arithmetic
template <class T>
void max(std::enable_if_t<std::is_arithmetic_v<T>, T> a, T b) {
    if (a > b) {
        std::cout << "max: " << a << '\n';
    } else {
        std::cout << "max: " << b << '\n';
    }
}

1
2
3
4
5
6
// The overload fails if the first parameter is arithmetic
template <class T>
void max(std::enable_if_t<!std::is_arithmetic_v<T>, T> a, T b) {
    std::cout << "a: " << a << '\n';
    std::cout << "b: " << b << '\n';
}

1
2
max(5, 7);         // call overload for arithmetic types
max("foo", "bar"); // call overload for non-arithmetic types

1
2
3
4
// The return type (bool) is only valid if T is an integral type:
template <class T> std::enable_if_t<std::is_integral_v<T>, bool> is_odd(T i) {
    return bool(i % 2);
}

1
2
3
4
5
// The second template argument is only valid if T is an integral type
template <class T, class = std::enable_if_t<std::is_integral_v<T>>>
bool is_even(T i) {
    return !bool(i % 2);
}

1
2
3
std::cout << "i is odd: " << is_odd(1) << '\n'; // is_odd(1.2) wouldn't work
std::cout << "i is even: " << is_even(1)
          << '\n'; // is_even(1.2) wouldn't work

1
2
3
4
std::cout << "int: " << std::is_floating_point<int>::value << '\n';
std::cout << "float: " << std::is_floating_point<float>::value << '\n';
std::cout << "const double: " << std::is_floating_point<const double>::value
          << '\n';

1
2
3
4
5
6
std::cout << "int: " << std::is_array<int>::value << '\n';
std::cout << "int[3]: " << std::is_array<int[3]>::value << '\n';
std::cout << "array<int,3>: " << std::is_array<std::array<int, 3>>::value
          << '\n';
std::cout << "string: " << std::is_array<std::string>::value << '\n';
std::cout << "string[3]: " << std::is_array<std::string[3]>::value << '\n';

1
2
3
4
5
6
7
std::cout << "A: " << std::is_class<A>::value << '\n';
std::cout << "B: " << std::is_class<B>::value << '\n';
std::cout << "C: " << std::is_class<C>::value << '\n';
std::cout << "D: " << std::is_class<D>::value << '\n';
std::cout << "A: " << std::is_enum<A>::value << '\n';
std::cout << "B: " << std::is_enum<B>::value << '\n';
std::cout << "C: " << std::is_enum<C>::value << '\n';

1
2
3
4
5
std::cout << "is_odd: " << std::is_function<decltype(is_odd<int>)>::value
          << '\n';
std::cout << "id_even: " << std::is_function<decltype(is_even<int>)>::value
          << '\n';
std::cout << "decltype(c): " << std::is_function<C>::value << '\n';

1
2
3
std::cout << "char: " << std::is_integral<char>::value << '\n';
std::cout << "int: " << std::is_integral<int>::value << '\n';
std::cout << "float: " << std::is_integral<float>::value << '\n';

1
2
3
4
5
std::cout << "int: " << std::is_pointer<int>::value << '\n';
std::cout << "int*: " << std::is_pointer<int *>::value << '\n';
std::cout << "int**: " << std::is_pointer<int **>::value << '\n';
std::cout << "int(*)(int): " << std::is_pointer<int (*)(int)>::value
          << '\n';

1
2
3
4
5
6
7
8
9
std::cout << "int: " << std::is_scalar<int>::value << '\n';
std::cout << "A: " << std::is_scalar<A>::value << '\n';
std::cout << "A&: " << std::is_scalar<A &>::value << '\n';
std::cout << "A*: " << std::is_scalar<A *>::value << '\n';
std::cout << "int(int): " << std::is_scalar<int(int)>::value << '\n';
std::cout << "int(*)(int): " << std::is_scalar<int (*)(int)>::value << '\n';
std::cout << "char: " << std::is_arithmetic<char>::value << '\n';
std::cout << "float: " << std::is_arithmetic<float>::value << '\n';
std::cout << "float*: " << std::is_arithmetic<float *>::value << '\n';

Share Snippets