39

Is there any way to check if an arbitrary variable type is iterable?

So to check if it has indexed elements or I can actually loop over it's children? (Use foreach for example?)

Is it possible to create a universal template for that?

I've found techniques for other programming languages while searching for it. Yet still have to find out how to do this in C++.

xskxzr
  • 12,442
  • 12
  • 37
  • 77
Roy Nieterau
  • 1,226
  • 2
  • 15
  • 26
  • Assuming iterability is expressed as a type, maybe RTTI? http://en.wikibooks.org/wiki/C%2B%2B_Programming/RTTI – RonaldBarzell Dec 11 '12 at 23:21
  • Could you provide an example or two of what you're asking? – Richard Dec 11 '12 at 23:31
  • I'm using the Maya API and it's classes. You can iterate over its MVectorArray, MIntArray classes, but also the MVector, MPoint, MColor because they contain more than a single numerical value (or string for the MString/MStringArray). Now I'm trying to create a template that supports adding (or multiplying, or dividing and more generic mathematical equations) any of the arbitrary values together. Which would mean for iterable elements to iterate over them and add a single element to each of its elements/indices. Does that make any sense? – Roy Nieterau Dec 12 '12 at 07:25
  • possible duplicate of [Match iterable types (arrays and classes with begin()/end())](http://stackoverflow.com/questions/25224002/match-iterable-types-arrays-and-classes-with-begin-end) – Pietro Saccardi May 12 '15 at 15:31

6 Answers6

40

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.

Jarod42
  • 203,559
  • 14
  • 181
  • 302
  • This is such a good example, it tought me a real good use of `std::declval`. Thank you very much! – PuerNoctis May 15 '16 at 16:44
  • 1
    this is `SFINE` for functions, and with `decltype` like above is the most elegant way for many different problems (why do we need any other way? use this and `enable_if` for everything). There is just one side note I have seen [here in the comments](http://stackoverflow.com/questions/23547654/sfinae-check-if-expression-compiles-and-return-stdtrue-type) about the need to stuff `void()` to break overloading `operator,`. It is not needed for this answer, but in general may be necessary in some cases. – dashesy Jul 11 '16 at 03:26
  • 1
    @Jarod42, why do you have an `int` as the argument type in the declaration `auto is_iterable_impl(int)`? Also, what is the evil operator here? – user2635088 Dec 01 '17 at 05:36
  • @user2635088: `int` is used to provide order when both overload are viable. `f(int)` is a better match than `f(...)`. – Jarod42 Dec 01 '17 at 08:39
  • We can overload `operator ,`, doing so might invalidate the test whereas it would be valid. (No match for `A(), B()` but `A()` and `B()` are both valid). – Jarod42 Dec 01 '17 at 08:42
  • 1
    Thank you for this great answer! Without it, I would never be able to create [this project](https://github.com/chariguanaCode/universal-print-in-cpp). – Adam Jeliński Apr 10 '20 at 16:28
  • This is a good example I found in my search. But I am looking for a way to determine if a variable (or object) is iterable. So, something like `auto n;` `is_iterable(n)`. Is that possible? If yes then how? – Abdur Rakib Jan 04 '22 at 10:18
  • @AbdurRakib: `template bool is_iterable_func(const T&) { return is_iterable::value; }` or `is_iterable::value`? – Jarod42 Jan 04 '22 at 10:32
17

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

Ted Lyngmo
  • 93,841
  • 5
  • 60
  • 108
aniliitb10
  • 1,259
  • 10
  • 14
8

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.

ABu
  • 10,423
  • 6
  • 52
  • 103
6

Yes using this traits class compatible

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

Community
  • 1
  • 1
oHo
  • 51,447
  • 27
  • 165
  • 200
5

It depends on what you mean by "iterable". It is a loose concept in C++ since you could implement iterators in many different ways.

If by foreach you're referring to C++11's range-based for loops, the type needs begin() and end() methods to be defined and to return iterators that respond to operator!=, operator++ and operator*.

If you mean Boost's BOOST_FOREACH helper, then see BOOST_FOREACH Extensibility.

If in your design you have a common interface that all iterable containers inherit from, then you could use C++11's std::is_base_of:

struct A : IterableInterface {}
struct B {}
template <typename T>
constexpr bool is_iterable() {
    return std::is_base_of<IterableInterface, T>::value;
}
is_iterable<A>(); // true
is_iterable<B>(); // false
Reuben Morais
  • 1,013
  • 1
  • 8
  • 20
1

Or if (like me) you hate every SFINAE solution being a big block of dummy struct definitions with ::type and ::value nonsense to wade through, here's an example of using a quick and (very) dirty one-liner:

template <
    class Container,
    typename ValueType = decltype(*std::begin(std::declval<Container>()))>
static void foo(Container& container)
{
    for (ValueType& item : container)
    {
        ...
    }
}

The last template argument does multiple things in one step:

  1. Checks to see if the type has a begin() member function, or equivalent.
  2. Checks that the begin() function returns something that has operator*() defined (typical for iterators).
  3. Determines the type that results from de-referencing the iterator, and saves it in case it's useful in your template implementation.

Limitation: Doesn't double-check that there's a matching end() member function.

If you want something more robust/thorough/reusable, then go with one of the other excellent proposed solutions instead.

BTJ
  • 188
  • 2
  • 5
  • And in addition, what you propose here is bad style. You write the metafunction inline, intermingled with your implementation code. If you happen to need it several times, you will end up duplicating the metafunction code into each and every usage site. In what way do you think that is "better" than defining a clean self-contained traits template, and just call it as a library metafunction when needed? Note: the latter approach is what you put off as "nonsense to wade through" – Ichthyo Feb 20 '20 at 21:44