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
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
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
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:
false
.42
has type int
, and thus int
is a better match than unsigned
, getting true
.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.
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:
begin()
member function, or equivalent.begin()
function returns something that has operator*()
defined (typical for iterators).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.
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) };
};
check<C>(0)
calls check(int,const_iterator)
if C::end()
exists and returns a const_iterator
compatible typecheck<C>(0)
calls check(...)
(see ellipsis conversion)sizeof(check<C>(0))
depends on the return type of these functionsvalue
to true
or false
#include <iostream>
#include <set>
int main()
{
std::cout <<"set="<< is_iterable< std::set<int> >::value <<'\n';
std::cout <<"int="<< is_iterable< int >::value <<'\n';
}
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
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.