Constraint style

From SFINAE to concepts and if constexpr.

SFINAE via enable_if and tag dispatch: powerful, unreadable, and famous for its error messages.

template <class T>
typename std::enable_if<std::is_integral<T>::value, T>::type
midpoint(T a, T b) { return a + (b - a) / 2; }

enable_if_t and the _t alias family cut the typename ...::type noise; variable templates set up the _v shorthands.

template <class T, class = std::enable_if_t<std::is_integral<T>::value>>
T midpoint(T a, T b);

if constexpr deletes most tag dispatch; void_t is the peak of detection-idiom SFINAE — the last hurrah before concepts.

template <class T>
auto size_of(const T& c) {
  if constexpr (requires_c<T>) return c.size();   // conceptually...
  else return distance(begin(c), end(c));
}

Concepts replace the whole stack: requires clauses and expressions, constrained auto, abbreviated templates. Constraints become readable, diagnosable, and overloadable.

template <class T>
concept Hashable = requires(const T& t) {
  { std::hash<T>{}(t) } -> std::convertible_to<std::size_t>;
};
void insert(Hashable auto&& key);

Minor polish year: constraints stay the C++20 shape; if consteval covers the compile-time-context branch that if constexpr cannot.

Pack indexing (T...[0]) and reflection start replacing trait metaprogramming inside constraints — querying members directly instead of detecting them indirectly.

template <class... Ts>
using first_t = Ts...[0];        // C++26 pack indexing, no recursion
C++29direction · not adopted

Direction: universal template parameters and reflection-driven concept definitions are the active threads; the C++20 surface is expected to stay stable.

C++26 per the final working draft; C++29 rows are proposals and direction, not adopted features.