There are many ways to trigger SFINAE, being enable_if
just one of them.
First of all:
What is std::enable_if ?
It's just this:
template<bool, class T=void> enable_if{ typedef T type; };
template<class T> enable_if<false,T> {};
template<bool b, class T=void> using enable_if_t = typename enable_f<b,T>::type;
The idea is to make typename enable_if<false>::type
to be an error, hence make any template declaration containing it skipped.
So how can this trigger function selection?
Disabling functions
The idea is making the declaration erroneous in some part:
By return type
template<class Type>
std::enable_if_t<cond<Type>::value,Return_type> function(Type);
By a actual parameter
template<class Type>
return_type function(Type param, std::enable_if_t<cond<Type>::value,int> =0)
By a template parameter
template<class Type,
std::enable_if_t<cond<Type>::value,int> =0> //note the space between > and =
return_type function(Type param)
Selecting functions
You can parametrise different alternatives with tricks like this:
tempplate<int N> struct ord: ord<N-1>{};
struct ord<0> {};
template<class T, std::enable_if<condition3, int> =0>
retval func(ord<3>, T param) { ... }
template<class T, std::enable_if<condition2, int> =0>
retval func(ord<2>, T param) { ... }
template<class T, std::enable_if<condition1, int> =0>
retval func(ord<1>, T param) { ... }
template<class T> // default one
retval func(ord<0>, T param) { ... }
// THIS WILL BE THE FUCNTION YOU'LL CALL
template<class T>
retval func(T param) { return func(ord<9>{},param); } //any "more than 3 value"
This will call the first/second/third/fourth function if condition3
is satisfied, than condition2
than condition1
than none of them.
Other SFINAE triggers
Writing compile-time conditions can be either a matter of explicit specialization or a matter of unevaluated expression success/failure:
for example:
template<class T, class = void>
struct is_vector: std::false_type {};
template<class X>
struct is_vector<vector<X> >:: std::true_type {};
so that is_vector<int>::value
is false
but is_vecttor<vector<int> >::value
is true
Or, by means of introspection, like
template<class T>
struct is_container<class T, class = void>: std::false_type {};
template<class T>
struct is_container<T, decltype(
std::begin(std::declval<T>()),
std::end(std::declval<T>()),
std::size(std::declval<T>()),
void(0))>: std::true_type {};
so that is_container<X>::value
will be true
if given X x
, you can compile std::begin(x)
etc.
The trick is that the decltype(...)
is actually void
(the ,
operator discards the previous expressions) only if all the sub-expressions are compilable.
There can be even many other alternatives. Hope between all this you can find something useful.