Why is this an issue?
std::enable_if
is a very important part of template meta-programming in C++ up to C++17. Based on SFINAE, 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. Since C++20, new features offer first-class support for what used to require enable_if
trickery:
- 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 {rule:cpp:S6017}).
Additionally, since those features provide a higher level of abstraction, compilers understand them better and can provide clearer diagnostics when
a constraint is violated.
As a consequence, std::enable_if
is no longer the right tool and should be replaced with those facilities. Note that the replacement
is not always mechanical: The expression controlling a std::enable_if
would probably be acceptable as a requires
condition,
but better alternatives usually exist, for instance reusing an existing concept defined in the standard.
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; }
Resources
Why I want Concepts, and why I want them sooner rather than
later
Related rules
- {rule:cpp:S6017} to see when
std::enable_if
could be replaced with if constexpr
.