|
SpECTRE
v2025.08.19
|
SFINAE, Substitution Failure Is Not An Error, means that if a deduced template substitution fails, compilation must continue. This can be exploited to make decisions at compile time. See here for a discussion using std::enable_if to remove certain functions from overload resolution or certain template specializations from name lookup. Another method of controlling name lookup resolution is using std::void_t. void_t is a metafunction from types to void, that is
void_t is useful when used in combination with decltype and std::declval to probe if a type has certain members. For example, we can implement a type trait to check if a type T is iterable by first have the general definition inherit from std::false_type as follows,
Next we will have specialization that uses void_t to check if the type T has a begin() and end() function.
What is happening here? Well, we use std::declval to convert the type T to a reference type, which allows us to call member functions inside decltype expressions without having to construct an object. First we try to call the member function begin() on std::declval<T>(), and if that succeeds we throw away the result using the comma operator. Next we try to call end() on std::declval<T>(), which, if it succeeds we get the return type of using decltype. Note that decltype is important because we can only call member functions on reference types inside of decltype, not just anywhere. Finally, if all this succeeded use void_t to metareturn void, otherwise the template parameters of void_t fail to evaluate and the specialization cannot be resolved during name lookup. We could just as well use
Which of the two implementations of the is_iterable is preferred is simply a matter of taste, both behave correctly.
If you're reading closely you might wonder why the void_t is necessary at all, why not just decltype(...)? Well the reason is that since the default template parameter metavalue is void, the specialization cannot be resolved during name lookup unless the second template parameter in the specialization is either void as well or is explicitly specified when the class template is being invoked. Thus, the clearest implementation probably is
You could now also define a helper type alias and constexpr boolean