std::enable_if
is a very important part of template meta-programming in C++ up to C++17. Based on SFINAE ("Substitution Failure Is Not
An Error"), it can be used to subtly tune the behavior of overload resolution based on properties of types.
However, using std::enable_if
correctly is not easy and requires skills and experience for a resulting code that is not
straightforward and costly to maintain. Since C++20, new features replace complex uses of std::enable_if
:
- Concepts allow defining named constraints on types, using a terse syntax to specify that a template argument must adhere to a concept;
-
requires
clauses can be directly written for one-shot constraints;
- In some cases, using
if constexpr
(introduced in C++17) may replace an overload set with just one function (see S6017).
Additionally, since those features provide a higher level of abstraction, compilers understand them better and can provide more straightforward
diagnostics when a constraint is violated.
Consequently, std::enable_if
is no longer the right tool and should be replaced with those facilities. Note that the replacement is
not always mechanical. For instance, reusing an existing concept defined in the standard is a better alternative than putting the
std::enable_if
expression in a requires
clause.
This rule reports the use of std::enable_if
.
Noncompliant code example
template <typename N, class = typename
std::enable_if<std::is_integral_v<N> && std::is_signed_v<N>>::type> // Noncompliant
auto negate(N n) { return -n; }
Compliant solution
template <class N> requires std::signed_integral<N>
auto negate(N n) { return -n; }
Or
template <std::signed_integral N>
auto negate(N n) { return -n; }
Or
auto negate(std::signed_integral auto n) { return -n; }