In 2016, Andrzej Krzemieński pointed out that static_assert is great and all, but it doesn’t stop the cascade of error messages from something like this:

template<class It> void sort2(It first, It last) { static_assert(is_random_access_iterator_v<It>); std::sort(first, last); }

He suggested a neat trick to suppress the cascade of error messages: simply don’t compile the call to std::sort unless the static_assert would have passed. He did it with tag-dispatch; but in C++17 we can do it even more cleanly with if constexpr . Example:

template<class T> void sort2(It first, It last) { if constexpr (!is_random_access_iterator_v<T>) { static_assert(is_random_access_iterator_v<T>); } else { std::sort(first, last); } }

This second snippet yields exactly one error:

error: static_assert failed due to requirement 'is_random_access_iterator_v<int>' static_assert(is_random_access_iterator_v<T>); ^ ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ note: in instantiation of function template specialization 'sort2<int>' requested here sort2(1, 2); ^ 1 error generated.

which doesn’t seem impressive at all, until you compile the first snippet and see its cascade of 16 errors, including such beauties as

In file included from /opt/compiler-explorer/gcc-7.2.0/include/c++/7.2.0/algorithm:62: /opt/compiler-explorer/gcc-7.2.0/include/c++/7.2.0/bits/stl_algo.h:81:11: error: no matching function for call to object of type '__gnu_cxx::__ops::_Iter_less_iter' if (__comp(__a, __b)) ^~~~~~ /opt/compiler-explorer/gcc-7.2.0/include/c++/7.2.0/bits/stl_algo.h:1921:12: note: in instantiation of function template specialization 'std::__move_median_to_first<int, __gnu_cxx::__ops::_Iter_less_iter>' requested here std::__move_median_to_first(__first, __first + 1, __mid, __last - 1, ^ /opt/compiler-explorer/gcc-7.2.0/include/c++/7.2.0/bits/stl_algo.h:1953:11: note: in instantiation of function template specialization 'std::__unguarded_partition_pivot<int, __gnu_cxx::__ops::_Iter_less_iter>' requested here std::__unguarded_partition_pivot(__first, __last, __comp); ^ /opt/compiler-explorer/gcc-7.2.0/include/c++/7.2.0/bits/stl_algo.h:1968:9: note: in instantiation of function template specialization 'std::__introsort_loop<int, int, __gnu_cxx::__ops::_Iter_less_iter>' requested here std::__introsort_loop(__first, __last, ^ /opt/compiler-explorer/gcc-7.2.0/include/c++/7.2.0/bits/stl_algo.h:4836:12: note: in instantiation of function template specialization 'std::__sort<int, __gnu_cxx::__ops::_Iter_less_iter>' requested here std::__sort(__first, __last, __gnu_cxx::__ops::__iter_less_iter()); ^

In the comments, Sergey Vidyuk suggests a neat (or maybe just “cute”) hack if you want to use enable_if SFINAE but don’t like typing out enable_if or deciding where to put it in your function’s signature. (Unlike the previous snippets, this does SFINAE on failure, rather than erroring out.)

template<class T, class = std::enable_if_t<is_random_access_iterator_v<T>>> using RandomAccessIterator = T; template<class T> void sort2(RandomAccessIterator<T> b, RandomAccessIterator<T> e) { std::sort(b, e); }

Here’s a Godbolt comparing the error messages we get from each of the three snippets above. The really cool thing is that Clang is smart enough to recognize our Concepts-like idiom in the third snippet: