SFINAE
“substitution Failure Is Not An Error”
A lot of the code for ImReflect has to do with template meta programming. As explained in Library Design / Tag invoke, the compiler has to find a suitable tag_invoke implementation for each given type that goes into the single entry point.
This means, if there are two tag_invoke functions, that both “accept” the same type, the compiler will throw an error, not knowing which one to pick. For example:
void tag_invoke(int& value){
// do this
}
void tag_invoke(int& value){
// do that
}
int a = 42;
tag_invoke(a); // ERROR: doesn't know which one to pick
Now this example is easy to solve. Just remove one of the implementations. But it gets a bit more complicated when working with templates. For example, we want a function only for integral types char, short, int, etc. and a function only for floating point numbers float, double, long double.
To do this, we can use SFINAE to “remove/disable” functions for specific types. we can do this with
std::enable_if_t<CONDITION, RETURN VALUE>
This means that a function only works if the condition inside std::enable_if_t is true.
For example:
template<typename T>
std::enable_if_t<std::is_integral_v<T>, void> tag_invoke(T& value){
// integral types
printf("integral type")
}
template<typename T>
std::enable_if_t<std::is_floating_point_v<T>, void> tag_invoke(T& value){
// floating point types
printf("floating point type");
}
std::is_integral_vreturns true when it’s a type likeint,char,short, etc.std::is_floating_point_vreturn true when it’s a floating point type,float,double,long double.
From xtr1common:
_EXPORT_STD template <class _Ty>
constexpr bool is_integral_v = _Is_any_of_v<remove_cv_t<_Ty>, bool, char, signed char, unsigned char, wchar_t,
#ifdef __cpp_char8_t
char8_t,
#endif // defined(__cpp_char8_t)
char16_t, char32_t, short, unsigned short, int, unsigned int, long, unsigned long, long long, unsigned long long>;
_EXPORT_STD template <class _Ty>
constexpr bool is_floating_point_v = _Is_any_of_v<remove_cv_t<_Ty>, float, double, long double>;
So when I call:
tag_invoke(42); // prints: "integral type"
tag_invoke(3.14f) // printf: "floating point type"
It correctly finds the suitable function! SFINAE allows us to do if checks on function (and more) at compile time.
See my next blog post about how we can combine SFINAE with Type Traits