So, here is the answer entirely from the left margin.
You must rotate your types A and B into iterable objects, overloading begin and end in their namespace.
There are several approaches for this:
1) You state that each of them implements member- begin and member- end , which returns a pointer as an iterator. Alternatively, the free begin and end function does the same.
2) You need them to inherit from the helper class CRTP, which does this: it either implements begin and end for you, or allows overloading the begin and end function, which can with ADL.
3) If all such classes are in some namespace that you control, and you want to process the fields K* p and std::size_t n as evidence that it should be considered as an iterable range, then we can do this with "global " begin and end , which SFINAE uses to apply only in this case.
I would recommend # 1 or # 2.
For # 2:
template<typename Derived> struct p_n_iterable { Derived* self() { static_assert( std::is_base_of<p_n_iterable, Derived>::value, "CRTP failure" ); return static_cast<Derived*>(this); } Derived const* self() const { static_assert( std::is_base_of<p_n_iterable, Derived>::value, "CRTP failure" ); return static_cast<Derived const*>(this); } typedef typename std::decay< decltype( *Derived::p ) >::type value_type; typedef value_type* iterator; std::size_t size() const { return self()->n; } iterator begin() { return self->p; } iterator end() { return begin() + size(); } };
if I wrote this right, change A to:
template<typename K> struct A : p_n_iterable<A<K>> { ... unchanged ... };
and suddenly loops like for( auto x:a ) work on A
I think the cost of adding this little preamble to classes is worth using.
To do this with # 3, we create a traits class that determines whether it should be n_p_iterable , checking that T::n is of type std::size_t and T::p is a pointer type. I would advise against this, because although it requires fewer templates elsewhere, it is pretty hacky.
Once we get this, we can write a really generic to_vector .
First, write yourself get_iterator_type<Container> :
namespace adl_helper { using std::begin; using std::end; template<typename C> auto adl_begin(C&& c)->decltype(begin( std::forward<C>(c) )); template<typename C> auto adl_end(C&& c)->decltype(end( std::forward<C>(c) )); } using adl_helper::adl_begin; using adl_helper::adl_end; template<typename... Ts> struct type_sink { typedef void type; } template<typename... Ts> using TypeSink = typename type_sink<Ts...>::type; template<typename Container, typename=void> struct get_iterator_type {}; template<typename Container> struct get_iterator_type< Container, TypeSink< adl_begin( std::declval<Container&>() ) > > { typedef adl_begin( std::declval<Container&>() ) type; }; template<typename Container, typename=void> struct get_value_type {}; template<typename Container> struct get_value_type< Container, TypeSink< std::iterator_traits< typename get_iterator_type<Container>::type > > > { typedef std::iterator_traits< typename get_iterator_type<Container>::type > > traits; typedef typename traits::value_type type; };
write our to_vector :
template<typename C> auto to_vector( C&& container )-> std::vector<typename get_value_type<typename remove_reference<C>::type>::type> { std::vector<typename get_value_type<typename remove_reference<C>::type>::type> retval; for( auto&& x : std::forward<C>(container) ) { retval.push_back(x); } return retval; }
and if I dotted all i and crossed all t , you now have both a complete iteration of C ++ 11 style for your types, and to_vector , which works on both your types and other containers, such as a std::map ).
Further improvements may include determining whether the container passing through it has size or has random access iterators, and if so, reserve the size in retval . But this post is long enough.