namespace details { template<template<class...>class Z, class, class...Ts> struct can_apply:std::false_type{}; template<template<class...>class Z, class...Ts> struct can_apply<Z, std::void_t<Z<Ts...>>, Ts...>:std::true_type{}; }; template<template<class...>class Z, class...Ts> using can_apply = details::can_apply<Z, void, Ts...>; template<class T> using bar_r = decltype( bar( std::declval<T>() ) ); template<class T> using can_bar = can_apply<bar_r, T>;
this gives us a can_bar
, which true
iff bar(t)
is a valid expression.
template<class T> std::enable_if_t< can_bar<T>{}, int> =0 > void foo( T t ) {}
which has the advantage that SFINAE will not spoil the visible signature of the foo
function. The converse is easy:
template<class T> std::enable_if_t< !can_bar<T>{}, int> =0 > void foo( T t ) {}
Another advantage of this technique.
Note that you may want can_bar<T&>
instead of T
if you call it in the lvalue context rather than forwarding it.
Some of the above are C ++ 11.
It uses the C ++ 14 enable_if_t
function. Replace enable_if_t<blah>
with typename enable_if<blah>::type
in C ++ 11 or write your own enable_if_t
.
It also uses the C ++ 14 function std::void_t
.
template<class...>struct voider{using type=void;}; template<class...Ts>using void_t = typename voider<Ts...>::type;
to write it in C ++ 11.
is_detected
, which on a track for C ++ 20 is similar to can_apply
above.