Check if a variable type is iterable?

You may create a trait for that:

namespace detail
{
    // To allow ADL with custom begin/end
    using std::begin;
    using std::end;

    template <typename T>
    auto is_iterable_impl(int)
    -> decltype (
        begin(std::declval<T&>()) != end(std::declval<T&>()), // begin/end and operator !=
        void(), // Handle evil operator ,
        ++std::declval<decltype(begin(std::declval<T&>()))&>(), // operator ++
        void(*begin(std::declval<T&>())), // operator*
        std::true_type{});

    template <typename T>
    std::false_type is_iterable_impl(...);

}

template <typename T>
using is_iterable = decltype(detail::is_iterable_impl<T>(0));

Live example.


Yes using this traits class compatible c++03

template<typename C>
struct is_iterable
{
  typedef long false_type; 
  typedef char true_type; 
    
  template<class T> static false_type check(...); 
  template<class T> static true_type  check(int, 
                    typename T::const_iterator = C().end()); 
    
  enum { value = sizeof(check<C>(0)) == sizeof(true_type) }; 
};

Explanation

  • check<C>(0) calls check(int,const_iterator) if C::end() exists and returns a const_iterator compatible type
  • else check<C>(0) calls check(...) (see ellipsis conversion)
  • sizeof(check<C>(0)) depends on the return type of these functions
  • finally, the compiler sets the constant value to true or false

See compilation and test run on coliru

#include <iostream>
#include <set>

int main()
{
    std::cout <<"set="<< is_iterable< std::set<int> >::value <<'\n';
    std::cout <<"int="<< is_iterable< int           >::value <<'\n';
}

Output

set=1
int=0

Note: C++11 (and C++14) provides many traits classes but none about iterablility...

See also similar answers from jrok and Jarod42.

This answer is in Public Domain - CC0 1.0 Universal


cpprefence has an example answering your question. It is using SFINAE, here is a slightly modified version of that example (in case the content of that link gets changed over time):

template <typename T, typename = void>
struct is_iterable : std::false_type {};

// this gets used only when we can call std::begin() and std::end() on that type
template <typename T>
struct is_iterable<T, std::void_t<decltype(std::begin(std::declval<T>())),
                                  decltype(std::end(std::declval<T>()))
                                 >
                  > : std::true_type {};

// Here is a helper:
template <typename T>
constexpr bool is_iterable_v = is_iterable<T>::value;

Now, this is how it can be used

std::cout << std::boolalpha;
std::cout << is_iterable_v<std::vector<double>> << '\n';
std::cout << is_iterable_v<std::map<int, double>> << '\n';
std::cout << is_iterable_v<double> << '\n';
struct A;
std::cout << is_iterable_v<A> << '\n';

Output:

true
true
false
false

Having said that, all it checks is, the declaration of begin() const and end() const, so accordingly, even following is verified as an iterable:

struct Container
{
  void begin() const;
  void end() const;
};

std::cout << is_iterable_v<Container> << '\n'; // prints true

You can see these pieces together here


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.