I tried two methods to implement conversion from a const_iterator to an iterator. All iterators are based on boost/iterator.
Method 1 defines a iterator<T> class. iterator<const T> would represent a const_iterator. iterator<T> has a conversion operator that returns a iterator<const T>. This fails for template function because no type conversion can happen during template instantiation.
Method 2 works in theory. In practice, I need to define every method for the iterator<T>:
#include <iostream>
#include <boost/iterator/iterator_adaptor.hpp>
#include <vector>
template<typename Container>
class Cit
: public boost::iterator_adaptor<
Cit<Container>, // Derived
typename Container::const_iterator, // Base
const typename Container::value_type> {
using self_type = Cit<Container>;
friend class boost::iterator_core_access;
public:
explicit Cit(typename Container::const_iterator it)
: self_type::iterator_adaptor_(it) {}
};
template<typename Container>
class It : public Cit<Container> {
protected:
using reference = typename Container::reference;
using self_type = It<Container>;
using Base = Cit<Container>;
public:
explicit It(typename Container::iterator it)
: Base(it) {}
reference operator*() const {
return const_cast<reference>(Base::operator*());
}
// Try to hide every method from Cit<Container>
// ...
// ...
// ...
// ... oh well.
private:
friend class boost::iterator_core_access;
};
// A template function
template<typename Container>
void foo(Cit<Container> it_begin,
Cit<Container> it_end) {
for (auto it = it_begin; it != it_end; ++it) {
std::cout << *it << "\n";
}
}
int main() {
typedef std::vector<int> Container;
Container v = {0, 1, 2, 3}; // content array
It<Container> it_begin(v.begin());
It<Container> it_end(v.end());
// Assert It can implicitly convert to Cit even during template
// instantiation.
foo(it_begin, it_end);
return 0;
}
This seems to negate the benefits of using boost/iterator.
Is there a better way to make iterator and const_iterator with
boost/iterator?
Here is method 1:
#include <iostream>
#include <boost/iterator/iterator_adaptor.hpp>
#include <vector>
template<typename Container>
class It
: public boost::iterator_adaptor<
It<Container>, // Derived
typename Container::const_iterator, // Base
typename std::conditional<std::is_const<Container>::value,
const typename Container::value_type,
typename Container::value_type
>::type // Value
> {
using self_type = It<Container>;
friend class boost::iterator_core_access;
public:
explicit It(typename Container::const_iterator it)
: self_type::iterator_adaptor_(it) {}
};
template <typename C> using Cit = It<const C>;
// A template function
template<typename Container>
void foo(Cit<Container> it_begin,
Cit<Container> it_end) {
for (auto it = it_begin; it != it_end; ++it) {
std::cout << *it << "\n";
}
}
int main() {
typedef std::vector<int> Container;
Container v = {0, 1, 2, 3}; // content array
It<Container> it_begin(v.begin());
It<Container> it_end(v.end());
// Assert It can implicitly convert to from Cit to It even
// during template instantiation.
foo(it_begin, it_end);
return 0;
}
Error message:
error: no matching function for call to ‘foo(It<std::vector<int> >&, It<std::vector<int> >&)’
foo(it_begin, it_end);
^
main.cpp:26:6: note: candidate: template<class Container> void foo(Cit<Container>, Cit<Container>)
void foo(Cit<Container> it_begin,
^~~
main.cpp:26:6: note: template argument deduction/substitution failed:
main.cpp:41:25: note: types ‘const C’ and ‘std::vector<int>’ have incompatible cv-qualifiers
foo(it_begin, it_end);
I would specialize the template:
template <typename T>
class MyIt : public boost::iterator_adaptor<MyIt<T>, // Derived
typename T::iterator, // Base
typename T::reference> {
friend class boost::iterator_core_access;
public:
static constexpr bool is_const = false;
explicit MyIt(typename MyIt::base_type it) : MyIt::iterator_adaptor_(it) {}
};
template <typename T>
class MyIt<T const> : public boost::iterator_adaptor<MyIt<T const>, // Derived
typename T::const_iterator, // Base
typename T::const_reference> {
friend class boost::iterator_core_access;
public:
static constexpr bool is_const = true;
explicit MyIt(typename MyIt::base_type it) : MyIt::iterator_adaptor_(it) {}
};
Conversions are already allowed here, but if you want to have an explicit "to-const-cast" it's easy to write:
template <typename T>
static MyIt<T const> make_const(MyIt<T> it) { return MyIt<T const>(it.base()); }
Use it:
// A template function
template <typename It> void foo(It it_begin, It it_end) {
static_assert(It::is_const == std::is_const<typename std::remove_reference<decltype(*it_begin)>::type>::value, "mismatch");
if (It::is_const)
std::cout << "Const: ";
for (auto it = it_begin; it != it_end; ++it)
std::cout << *it << " ";
std::cout << "\n";
}
As you can see our function doesn't care about the specific iterator (this is the whole point of iterators). You can use it with const and non-const:
template <typename C> void foo(C const &c) {
MyIt<C const> b(c.begin()), e(c.end());
foo(b, e);
}
template <typename C> void foo(C &c) {
MyIt<C> b(c.begin()), e(c.end());
foo(b, e);
}
Quick Demo Live On Coliru
std::vector<int> v{ 0, 1, 2, 3 };
foo(v);
auto const &constv = v;
foo(constv);
Prints
void foo(C&) [with C = std::vector<int>]
0 1 2 3
void foo(const C&) [with C = std::vector<int>]
Const: 0 1 2 3
Forcing Const Iterators
This seems to be what your code is about. So, let's force that! It's a simple change of MyIt<C> to MyIt<C const>:
template <typename C> void foo(C &c) {
MyIt<C const> b(c.begin()), e(c.end()); // <--- note the const
foo(b, e);
}
Now foo will be called using const iterators even for non-const C. If you think that's subtle, you can use the helper shown above:
template <typename C> void foo(C &c) {
MyIt<C> b(c.begin()), e(c.end());
foo(make_const(b), make_const(e)); // <--- now more explicit?
}
Of course in foo you're free to modify the static_assert so that it just refuses to compile for non-const iterators in the first place:
// A template function
template <typename It> void foo(It it_begin, It it_end) {
static_assert(std::is_const<typename std::remove_reference<decltype(*it_begin)>::type>::value, "non-const disallowed");
if (It::is_const)
std::cout << "Const: ";
for (auto it = it_begin; it != it_end; ++it)
std::cout << *it << " ";
std::cout << "\n";
}
You can add an overload for any MyIt<> that does the constification:
template <typename C>
typename std::enable_if<!std::is_const<C>::value>::type
foo(MyIt<C> b, MyIt<C> e) {
foo(make_const(b), make_const(e));
}
So, now every invocation of foo is forced to const mode.
Full Listing
The last demo in full:
Live On Coliru
#include <boost/iterator/iterator_adaptor.hpp>
#include <iostream>
#include <vector>
template <typename T>
class MyIt : public boost::iterator_adaptor<MyIt<T>, // Derived
typename T::iterator, // Base
typename T::reference> {
friend class boost::iterator_core_access;
public:
static constexpr bool is_const = false;
explicit MyIt(typename MyIt::base_type it) : MyIt::iterator_adaptor_(it) {}
};
template <typename T>
class MyIt<T const> : public boost::iterator_adaptor<MyIt<T const>, // Derived
typename T::const_iterator, // Base
typename T::const_reference> {
friend class boost::iterator_core_access;
public:
static constexpr bool is_const = true;
explicit MyIt(typename MyIt::base_type it) : MyIt::iterator_adaptor_(it) {}
};
template <typename T>
static MyIt<T const> make_const(MyIt<T> it) { return MyIt<T const>(it.base()); }
// A template function
template <typename It> void foo(It it_begin, It it_end) {
static_assert(std::is_const<typename std::remove_reference<decltype(*it_begin)>::type>::value, "non-const disallowed");
if (It::is_const)
std::cout << "Const: ";
for (auto it = it_begin; it != it_end; ++it)
std::cout << *it << " ";
std::cout << "\n";
}
template <typename C>
typename std::enable_if<!std::is_const<C>::value>::type
foo(MyIt<C> b, MyIt<C> e) {
foo(make_const(b), make_const(e));
}
template <typename C> void foo(C &c) {
std::cout << __PRETTY_FUNCTION__ << "\n";
MyIt<C> b(c.begin()), e(c.end());
foo(b, e);
}
int main() {
std::vector<int> v{ 0, 1, 2, 3 };
foo(v);
auto const &constv = v;
foo(constv);
}
Which now prints:
void foo(C&) [with C = std::vector<int>]
Const: 0 1 2 3
void foo(C&) [with C = const std::vector<int>]
Const: 0 1 2 3
Related
Im trying to create a class as container-wrapper, something like this
#include <vector>
template <typename T, typename U>
class Test {
T<U> list;
};
int main() {
Test<std::vector, int> test;
}
But this wont work. I know it will work when I use it like this
#include <vector>
template <typename T>
class Test {
T list;
};
int main() {
Test<std::vector<int>> test;
}
but how would I write my insert?
template <typename T>
class Test {
T list;
public:
void insert(??? element) {
...
}
};
TIA, Narase
The easiest one would be
template <typename T>
class Test {
....
void insert(const typename T::value_type& element);
And/or, for completeness and move semantics compliance:
void insert(typename T::value_type&& element);
But this wont work.
With your first snippet of Test, since std::vector has two template parameters, value_type and allocator_type, we must specify the existence of these template parameters in the declaration of T using template template parameters as follows.
Here I use variadic one and then T<U...> becomes legal for
T = std::vector and compilation errors are removed.
how would I write my insert?
The following code also shows an implementation example of Test::insert.
Since this method can accept both lvalue and rvalue references in C++11 and over, here I apply the forwarding reference (which is what Scott Meyers calls universal reference,) to it.
In this approach, we does not need to specify the value type of Test::list in the Test::insert definition and passing wrong types to Test::insert causes compilation errors.
If you need the value type of std::vector, the member type std::vector::value_type is available:
template <template<class...> class T, class ...U>
class Test
{
T<U...> list;
public:
template<class ...V>
void insert(V&& ...element) {
list.insert(std::forward<V>(element)...);
}
typename T<U...>::iterator begin() noexcept {
return list.begin();
}
typename T<U...>::iterator end() noexcept {
return list.end();
}
using value_type = typename T<U...>::value_type;
};
This is an usage example with std::vector:
DEMO (std::vector)
static_assert(
std::is_same<Test<std::vector, int>::value_type, int>::value, "oops!"); // OK.
Test<std::vector, int> test; // OK
test.insert(test.end(), 11);
test.insert(test.end(), 99);
for(auto it = test.begin(); it != test.end(); ++it){
std::cout << *it << std::endl;
}
As another example, this wrapper class also works with std::map as follows:
DEMO (std::map)
static_assert(
std::is_same<Test<std::map, int, int>::value_type, std::pair<const int, int>>::value, "oops!"); // OK.
Test<std::map, int, int> test; // OK
test.insert(std::make_pair(1, 11));
test.insert(std::make_pair(2, 99));
for(auto it = test.begin(); it!= test.end(); ++it){
std::cout << it->first << ", " << it->second << std::endl;
}
Finally, this is a fixed version of your second snippet:
DEMO (std::vector)
DEMO (std::set)
template <class T>
class Test
{
T list;
public:
template<class ...V>
void insert(V&& ...element) {
list.insert(std::forward<V>(element)...);
}
typename T::iterator begin() noexcept {
return list.begin();
}
typename T::iterator end() noexcept {
return list.end();
}
using value_type = typename T::value_type;
};
I'd like to write a class test that is able to store a function that is able to iterate through a collection of elements identified by the classic [first,last) iterator pair, that is:
template <typename T>
struct sum
{
template <typename I>
T operator()(I first, I last) const
{
T res = 0;
while (first != last)
{
res += *first;
++first;
}
return res;
}
};
//...
int main()
{
test<double> t;
t.set(sum<double>);
double a[] {1.,2.,3.};
std::cout << "Test (array) => " << t.call(a, a+3) << std::endl;
std::vector<double> v {1.,2.,3.};
std::cout << "Test (vector) => " << t.call(v.begin(), v.end()) << std::endl;
std::list<double> l {1.,2.,3.};
std::cout << "Test (list) => " << t.call(l.begin(), l.end()) << std::endl;
}
I thought to use std::function, but I've failed to do this as I wasn't able to declare the templated iterator pair.
A possible workaround is the following, which however only works with plain arrays (e.g., double[] or double*, like the above variable a), but not with other containers (e.g., like the above variables v and l):
template <typename T>
class test
{
public:
template <typename F>
void set(F f)
{
f_ = f;
}
template <typename I>
T call(I first, I last) const
{
return f_(first, last);
}
private:
std::function<T(T*,T*)> f_;
};
Any idea on how can I get the correct behavior?
NOTE: I'm compiling with GCC 4.9.2 --std=c++11
Thank you very much.
What you want is really to be able to construct a:
std::function<T(FwdIter<T>, FwdIter<T>)>
where FwdIter<T> is some type-erased class that satsifes the ForwardIterator concept and is dereferenced to a T. For that, check out the Boost.TypeErasure library, where we can do:
#include <boost/type_erasure/any.hpp>
#include <boost/type_erasure/operators.hpp>
#include <boost/mpl/vector.hpp>
using namespace boost::type_erasure;
template <typename T>
using FwdIter = any<
boost::mpl::vector<
copy_constructible<>,
incrementable<>,
dereferenceable<T>,
equality_comparable<>
>>;
With that and your definition of sum, I can do:
std::function<int(FwdIter<int>, FwdIter<int>)> f = sum<int>{};
std::vector<int> v = {1, 2, 3, 4, 5};
std::cout << f(v.begin(), v.end()) << std::endl; // prints 15
In your test<T>, you could just have a std::function<T(FwdIter<T>, FwdIter<T>)> member as desired.
I've tried to work on an alternative solution.
Essentially, the user function is wrapped inside a holder holder which fix the function signature to T(const std::vector<T>&). With respect to #Barry's solution (the one I've accepted), this doesn't require external libraries. However it suffers of performance issues due to the construction of the vector object at runtime. Also, and more importantly, as pointed out by #Barry, this solution imposes artificial requirements on T (like T must be copyable).
Here is it:
template <typename T,typename F>
class holder
{
public:
holder(F f) : f_(f) { }
T operator()(const std::vector<T>& v) const
{
return f_(v.begin(), v.end());
}
private:
F f_;
};
template <typename T>
class test_v2
{
public:
template <typename F>
void set(F f)
{
f_ = holder<T,F>(f);
}
template <typename I>
T call(I first, I last) const
{
return f_(std::vector<T>(first, last));
}
private:
std::function<T(const std::vector<T>&)> f_;
};
Why not stoke the functor instead, something like:
template <typename T>
class test
{
public:
template <typename I>
auto call(I first, I last) const
-> decltype(T()(first, last))
{
return T()(first, last);
}
};
And use it:
test<sum<double>> t;
Live example
Suppose we have this template
template<typename Container, typename T>
bool contains (const Container & theContainer, const T & theReference) {
...
}
How can it be stated that, obviously the elements in container should be of type T?
Can this all be abbreviated (maybe in C++11)?
While other answers using value_type are correct , the canonical solution to this frequent problem is to not pass the container in the first place : use the Standard Library semantics, and pass a pair of iterators.
By passing iterators, you don't have to worry about the container itself. Your code is also much more generic : you can act on ranges, you can use reversed iterators, you can combine your template with other standard algorithms etc.. :
template<typename Iterator, typename T>
bool contains (Iterator begin, Iterator end, const T& value) {
...
}
int main(){
std::vector<int> v { 41, 42 };
contains(std::begin(v), std::end(v), 42);
};
If you want to check the type carried by Iterator, you can use std::iterator_traits :
static_assert(std::is_same<typename std::iterator_traits<Iterator>::value_type, T>::value, "Wrong Type");
(Note that this assertion is generally not needed : if you provide a value not comparable with T, the template will not compile in the first place)
The final template would look like :
template<typename Iterator, typename T>
bool contains (Iterator begin, Iterator end, const T& value) {
static_assert(std::is_same<typename std::iterator_traits<Iterator>::value_type, T>::value, "Wrong Type");
while(begin != end)
if(*begin++ == value)
return true;
return false;
}
Live demo
Notes:
1) This should not be a surprise, but our contains template now has almost the same signature than std::find (which returns an iterator) :
template< class InputIt, class T >
InputIt find( InputIt first, InputIt last, const T& value );
2) If modifying the signature of the original contains is too much, you can always forward the call to our new template :
template<typename Container, typename T>
bool contains (const Container & theContainer, const T & theReference) {
return contains(std::begin(theContainer), std::end(theContainer), theReference);
}
You might restrict the container type in the template:
#include <algorithm>
#include <iostream>
#include <vector>
template< template<typename ... > class Container, typename T>
bool contains(const Container<T>& container, const T& value) {
return std::find(container.begin(), container.end(), value) != container.end();
}
int main()
{
std::vector<int> v = { 1, 2, 3 };
std::cout << std::boolalpha
<< contains(v, 0) << '\n'
<< contains(v, 1) << '\n';
// error: no matching function for call to ‘contains(std::vector<int>&, char)’
contains(v, '0') ;
return 0;
}
A more complete solution (addressing some comments):
#include <algorithm>
#include <array>
#include <iostream>
#include <map>
#include <set>
#include <vector>
// has_member
// ==========
namespace Detail {
template <typename Test>
struct has_member
{
template<typename Class>
static typename Test::template result<Class>
test(int);
template<typename Class>
static std::false_type
test(...);
};
}
template <typename Test, typename Class>
using has_member = decltype(Detail::has_member<Test>::template test<Class>(0));
// has_find
// ========
namespace Detail
{
template <typename ...Args>
struct has_find
{
template<
typename Class,
typename R = decltype(std::declval<Class>().find(std::declval<Args>()... ))>
struct result
: std::true_type
{
typedef R type;
};
};
}
template <typename Class, typename ...Args>
using has_find = has_member<Detail::has_find<Args...>, Class>;
// contains
// ========
namespace Detail
{
template<template<typename ...> class Container, typename Key, typename ... Args>
bool contains(std::false_type, const Container<Key, Args...>& container, const Key& value) {
bool result = std::find(container.begin(), container.end(), value) != container.end();
std::cout << "Algorithm: " << result << '\n';;
return result;
}
template<template<typename ...> class Container, typename Key, typename ... Args>
bool contains(std::true_type, const Container<Key, Args...>& container, const Key& value) {
bool result = container.find(value) != container.end();
std::cout << " Member: " << result << '\n';
return result;
}
}
template<template<typename ...> class Container, typename Key, typename ... Args>
bool contains(const Container<Key, Args...>& container, const Key& value) {
return Detail::contains(has_find<Container<Key, Args...>, Key>(), container, value);
}
template<typename T, std::size_t N>
bool contains(const std::array<T, N>& array, const T& value) {
bool result = std::find(array.begin(), array.end(), value) != array.end();
std::cout << " Array: " << result << '\n';;
return result;
}
// test
// ====
int main()
{
std::cout << std::boolalpha;
std::array<int, 3> a = { 1, 2, 3 };
contains(a, 0);
contains(a, 1);
std::vector<int> v = { 1, 2, 3 };
contains(v, 0);
contains(v, 1);
std::set<int> s = { 1, 2, 3 };
contains(s, 0);
contains(s, 1);
std::map<int, int> m = { { 1, 1}, { 2, 2}, { 3, 3} };
contains(m, 0);
contains(m, 1);
return 0;
}
For standard container, you may use value_type:
template<typename Container>
bool contains (const Container & theContainer, const typename Container::value_type& theReference) {
...
}
Note that there is also const_reference in your case:
template<typename Container>
bool contains (const Container & theContainer, typename Container::const_reference theReference) {
...
}
You can check the value_type of container and T using static_assert
template<typename Container, typename T>
bool contains (const Container & theContainer, const T & theReference) {
static_assert( std::is_same<typename Container::value_type, T>::value,
"Invalid container or type" );
// ...
}
Demo Here
Using std::enable_if (http://en.cppreference.com/w/cpp/types/enable_if), but a little more complicated than with static_assert.
EDIT: According to P0W's comment, using std::enable_if allows us to use SFINAE, which is nice when you decide to have more overloads. For example if the compiler decides to use this templated function, with a Container with no value_type typedefed, it won't generate an error instantly, like static_assert would, just looks for other functions which perfectly fits the signature.
Tested on Visual Studio 12.
#include <vector>
#include <iostream>
template<typename Container, typename T>
typename std::enable_if<
std::is_same<T, typename Container::value_type>::value, bool>::type //returns bool
contains(const Container & theContainer, const T & theReference)
{
return (std::find(theContainer.begin(), theContainer.end(), theReference) != theContainer.end());
};
int main()
{
std::vector<int> vec1 { 1, 3 };
int i = 1;
float f = 1.0f;
std::cout << contains(vec1, i) << "\n";
//std::cout << contains(vec1, f); //error
i = 2;
std::cout << contains(vec1, i) << "\n";
};
output:
1
0
PS: Your original function does it too, except that allows derived classes too. These solutions does not.
I try to write a class that takes a tuple of functions as its argument and overloads operator() for all argument_types of the function. Right now this looks like this:
template<typename T>
struct holder {
T t;
};
template<typename T, std::size_t i>
struct overload_helper : public holder<T>, public overload_helper<T, i - 1> {
overload_helper(T t) : holder<T>({t}) {};
typedef typename std::tuple_element<i - 1, T>::type inner_type;
typename inner_type::result_type operator()(typename inner_type::argument_type x) {
return std::get<i - 1>(holder<T>::t)(x); }
};
template<typename T>
struct overload_helper<T, 1> {
typedef typename std::tuple_element<0 ,T>::type inner_type;
typename inner_type::result_type operator()(typename inner_type::argument_type x) {
return std::get<0>(holder<T>::t)(x); }
};
template<typename T>
struct overload : public overload_helper<T, std::tuple_size<T>::value>
{
typedef void result_type;
overload(const T& t) : overload_helper<T, std::tuple_size<T>::value>(t) { }
};
int main() {
auto all = make_overload(std::make_tuple(
std::function<void(double)>([](double i) {
std::cout << "double" << std::endl;
}), std::function<void(int)>([](int i) {
std::cout << "int" << std::endl; })));
all(1); //fails
all(1.0);
}
The problem is that the base class is hiding each recursive definition of operator(). Is it possible to recursively unhide all definitions with using or is the only way to have a templated operator() and pick the right overload with boost::mpl?
using overload_helper<T, i - 1>::operator() in each overload_helper should do the job, so long they aren't ambiguous.
Is there any existing iterator implementation (perhaps in boost) which implement some sort of flattening iterator?
For example:
unordered_set<vector<int> > s;
s.insert(vector<int>());
s.insert({1,2,3,4,5});
s.insert({6,7,8});
s.insert({9,10,11,12});
flattening_iterator<unordered_set<vector<int> >::iterator> it( ... ), end( ... );
for(; it != end; ++it)
{
cout << *it << endl;
}
//would print the numbers 1 through 12
I don't know of any implementation in a major library, but it looked like an interesting problem so I wrote a basic implementation. I've only tested it with the test case I present here, so I don't recommend using it without further testing.
The problem is a bit trickier than it looks because some of the "inner" containers may be empty and you have to skip over them. This means that advancing the flattening_iterator by one position may actually advance the iterator into the "outer" container by more than one position. Because of this, the flattening_iterator needs to know where the end of the outer range is so that it knows when it needs to stop.
This implementation is a forward iterator. A bidirectional iterator would also need to keep track of the beginning of the outer range. The flatten function templates are used to make constructing flattening_iterators a bit easier.
#include <iterator>
// A forward iterator that "flattens" a container of containers. For example,
// a vector<vector<int>> containing { { 1, 2, 3 }, { 4, 5, 6 } } is iterated as
// a single range, { 1, 2, 3, 4, 5, 6 }.
template <typename OuterIterator>
class flattening_iterator
{
public:
typedef OuterIterator outer_iterator;
typedef typename OuterIterator::value_type::iterator inner_iterator;
typedef std::forward_iterator_tag iterator_category;
typedef typename inner_iterator::value_type value_type;
typedef typename inner_iterator::difference_type difference_type;
typedef typename inner_iterator::pointer pointer;
typedef typename inner_iterator::reference reference;
flattening_iterator() { }
flattening_iterator(outer_iterator it) : outer_it_(it), outer_end_(it) { }
flattening_iterator(outer_iterator it, outer_iterator end)
: outer_it_(it),
outer_end_(end)
{
if (outer_it_ == outer_end_) { return; }
inner_it_ = outer_it_->begin();
advance_past_empty_inner_containers();
}
reference operator*() const { return *inner_it_; }
pointer operator->() const { return &*inner_it_; }
flattening_iterator& operator++()
{
++inner_it_;
if (inner_it_ == outer_it_->end())
advance_past_empty_inner_containers();
return *this;
}
flattening_iterator operator++(int)
{
flattening_iterator it(*this);
++*this;
return it;
}
friend bool operator==(const flattening_iterator& a,
const flattening_iterator& b)
{
if (a.outer_it_ != b.outer_it_)
return false;
if (a.outer_it_ != a.outer_end_ &&
b.outer_it_ != b.outer_end_ &&
a.inner_it_ != b.inner_it_)
return false;
return true;
}
friend bool operator!=(const flattening_iterator& a,
const flattening_iterator& b)
{
return !(a == b);
}
private:
void advance_past_empty_inner_containers()
{
while (outer_it_ != outer_end_ && inner_it_ == outer_it_->end())
{
++outer_it_;
if (outer_it_ != outer_end_)
inner_it_ = outer_it_->begin();
}
}
outer_iterator outer_it_;
outer_iterator outer_end_;
inner_iterator inner_it_;
};
template <typename Iterator>
flattening_iterator<Iterator> flatten(Iterator it)
{
return flattening_iterator<Iterator>(it, it);
}
template <typename Iterator>
flattening_iterator<Iterator> flatten(Iterator first, Iterator last)
{
return flattening_iterator<Iterator>(first, last);
}
The following is a minimal test stub:
#include <algorithm>
#include <iostream>
#include <set>
#include <vector>
int main()
{
// Generate some test data: it looks like this:
// { { 0, 1, 2, 3 }, { 4, 5, 6, 7 }, { 8, 9, 10, 11 } }
std::vector<std::vector<int>> v(3);
int i(0);
for (auto it(v.begin()); it != v.end(); ++it)
{
it->push_back(i++); it->push_back(i++);
it->push_back(i++); it->push_back(i++);
}
// Flatten the data and print all the elements:
for (auto it(flatten(v.begin(), v.end())); it != v.end(); ++it)
{
std::cout << *it << ", ";
}
std::cout << "\n";
// Or, since the standard library algorithms are awesome:
std::copy(flatten(v.begin(), v.end()), flatten(v.end()),
std::ostream_iterator<int>(std::cout, ", "));
}
Like I said at the beginning, I haven't tested this thoroughly. Let me know if you find any bugs and I'll be happy to correct them.
I decided to "improve" a bit on the flattening iterator concept, though as noted by James you are stuck using Ranges (except for the inner most container), so I just used ranges through and through and thus obtained a flattened range, with an arbitrary depth.
First I used a building brick:
template <typename C>
struct iterator { using type = typename C::iterator; };
template <typename C>
struct iterator<C const> { using type = typename C::const_iterator; };
And then defined a (very minimal) ForwardRange concept:
template <typename C>
class ForwardRange {
using Iter = typename iterator<C>::type;
public:
using pointer = typename std::iterator_traits<Iter>::pointer;
using reference = typename std::iterator_traits<Iter>::reference;
using value_type = typename std::iterator_traits<Iter>::value_type;
ForwardRange(): _begin(), _end() {}
explicit ForwardRange(C& c): _begin(begin(c)), _end(end(c)) {}
// Observers
explicit operator bool() const { return _begin != _end; }
reference operator*() const { assert(*this); return *_begin; }
pointer operator->() const { assert(*this); return &*_begin; }
// Modifiers
ForwardRange& operator++() { assert(*this); ++_begin; return *this; }
ForwardRange operator++(int) { ForwardRange tmp(*this); ++*this; return tmp; }
private:
Iter _begin;
Iter _end;
}; // class ForwardRange
This is our building brick here, though in fact we could make do with just the rest:
template <typename C, size_t N>
class FlattenedForwardRange {
using Iter = typename iterator<C>::type;
using Inner = FlattenedForwardRange<typename std::iterator_traits<Iter>::value_type, N-1>;
public:
using pointer = typename Inner::pointer;
using reference = typename Inner::reference;
using value_type = typename Inner::value_type;
FlattenedForwardRange(): _outer(), _inner() {}
explicit FlattenedForwardRange(C& outer): _outer(outer), _inner() {
if (not _outer) { return; }
_inner = Inner{*_outer};
this->advance();
}
// Observers
explicit operator bool() const { return static_cast<bool>(_outer); }
reference operator*() const { assert(*this); return *_inner; }
pointer operator->() const { assert(*this); return _inner.operator->(); }
// Modifiers
FlattenedForwardRange& operator++() { ++_inner; this->advance(); return *this; }
FlattenedForwardRange operator++(int) { FlattenedForwardRange tmp(*this); ++*this; return tmp; }
private:
void advance() {
if (_inner) { return; }
for (++_outer; _outer; ++_outer) {
_inner = Inner{*_outer};
if (_inner) { return; }
}
_inner = Inner{};
}
ForwardRange<C> _outer;
Inner _inner;
}; // class FlattenedForwardRange
template <typename C>
class FlattenedForwardRange<C, 0> {
using Iter = typename iterator<C>::type;
public:
using pointer = typename std::iterator_traits<Iter>::pointer;
using reference = typename std::iterator_traits<Iter>::reference;
using value_type = typename std::iterator_traits<Iter>::value_type;
FlattenedForwardRange(): _range() {}
explicit FlattenedForwardRange(C& c): _range(c) {}
// Observers
explicit operator bool() const { return static_cast<bool>(_range); }
reference operator*() const { return *_range; }
pointer operator->() const { return _range.operator->(); }
// Modifiers
FlattenedForwardRange& operator++() { ++_range; return *this; }
FlattenedForwardRange operator++(int) { FlattenedForwardRange tmp(*this); ++*this; return tmp; }
private:
ForwardRange<C> _range;
}; // class FlattenedForwardRange
And apparently, it works
I arrive a little late here, but I have just published a library (multidim) to deal with such problem. The usage is quite simple: to use your example,
#include "multidim.hpp"
// ... create "s" as in your example ...
auto view = multidim::makeFlatView(s);
// view offers now a flattened view on s
// You can now use iterators...
for (auto it = begin(view); it != end(view); ++it) cout << *it << endl;
// or a simple range-for loop
for (auto value : view) cout << value;
The library is header-only and has no dependencies. Requires C++11 though.
you can make one using iterator facade in boost.
I wrote iterator product which you can use as a template perhaps:
http://code.google.com/p/asadchev/source/browse/trunk/work/cxx/iterator/product.hpp
In addition to the answer of Matthieu, you can automatically count the amount of dimensions of the iterable/container. But first we must set up a rule when something is an iterable/container:
template<class T, class R = void>
struct AliasWrapper {
using Type = R;
};
template<class T, class Enable = void>
struct HasValueType : std::false_type {};
template<class T>
struct HasValueType<T, typename AliasWrapper<typename T::value_type>::Type> : std::true_type {};
template<class T, class Enable = void>
struct HasConstIterator : std::false_type {};
template<class T>
struct HasConstIterator<T, typename AliasWrapper<typename T::const_iterator>::Type> : std::true_type {};
template<class T, class Enable = void>
struct HasIterator : std::false_type {};
template<class T>
struct HasIterator<T, typename AliasWrapper<typename T::iterator>::Type> : std::true_type {};
template<class T>
struct IsIterable {
static constexpr bool value = HasValueType<T>::value && HasConstIterator<T>::value && HasIterator<T>::value;
};
We can count the dimensions as follows:
template<class T, bool IsCont>
struct CountDimsHelper;
template<class T>
struct CountDimsHelper<T, true> {
using Inner = typename std::decay_t<T>::value_type;
static constexpr int value = 1 + CountDimsHelper<Inner, IsIterable<Inner>::value>::value;
};
template<class T>
struct CountDimsHelper<T, false> {
static constexpr int value = 0;
};
template<class T>
struct CountDims {
using Decayed = std::decay_t<T>;
static constexpr int value = CountDimsHelper<Decayed, IsIterable<Decayed>::value>::value;
};
We then can create a view wrapper, that contains a begin() and end() function.
template<class Iterable, int Dims>
class Flatten {
public:
using iterator = FlattenIterator<Iterable, Dims>;
private:
iterator _begin{};
iterator _end{};
public:
Flatten() = default;
template<class I>
explicit Flatten(I&& iterable) :
_begin(iterable),
_end(iterable)
{}
iterator begin() const {
return _begin;
}
iterator end() const {
return _end;
}
};
To make the creation of the object Flatten a bit easier, we define a helper function:
template<class Iterable>
Flatten<std::decay_t<Iterable>, CountDims<Iterable>::value - 1> flatten(Iterable&& iterable) {
return Flatten<std::decay_t<Iterable>, CountDims<Iterable>::value - 1>(iterable);
}
Usage:
std::vector<std::vector<int>> vecs = {{1,2,3}, {}, {4,5,6}};
for (int i : flatten(vecs)) {
// do something with i
}