If you are under the umbrella of C++11 and beyond, one usual way of SFINAE checking that works when you have to specialize for just one property, is the following one:
template<class T, class = decltype(<expression that must compile>)>
inline constexpr bool expression_works(int) { return true; }
template<class>
inline constexpr bool expression_works(unsigned) { return false; }
template<class T, bool = expression_works<T>(42)>
class my_class;
template<class T>
struct my_class<T, true>
{ /* Implementation when true */ };
template<class T>
struct my_class<T, false>
{ /* Implementation when false */ };
The trick is as follow:
- When the expression doesn't work, only the second specialization will be instantiated, because the first will fail to compile and sfinae plays out. So you get
false
.
- When the expression works, both overloads are candidate, so I have to force a better specialization. In this case,
42
has type int
, and thus int
is a better match than unsigned
, getting true
.
- I take
42
because it's the answer to everything, inspired by Eric Niebler's range implementation.
In your case, C++11
has the free functions std::begin
and std::end
that works for arrays and containers, so the expression that must work is:
template<class T, class = decltype(std::begin(std::declval<T>()))
inline constexpr bool is_iterable(int) { return true; }
template<class>
inline constexpr bool is_iterable(unsigned) { return false; }
If you need more generality, a way to express that something is iterable could also include user-defined types that brings their own overloads for begin
and end
, so you need to apply some adl
here:
namespace _adl_begin {
using std::begin;
template<class T>
inline auto check() -> decltype(begin(std::declval<T>())) {}
}
template<class T, class = decltype(_adl_begin::check<T>())>
inline constexpr bool is_iterable(int) { return true; }
template<class>
inline constexpr bool is_iterable(unsigned) { return false; }
You can play with this technique to achieve solutions that fits better your actual context.