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; }
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
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.