I'm working on big codes for which performance matters. And one of the things I read is that raw loops should be avoided and replaced by for_each, range-based for loops, or STL algorithms etc etc. The problem is that in all (most) examples, everything looks adapted for the problem, i.e. for_each is shown with the cout routine *eye roll*.
In my case, the index inside the loop matters (unless you show me otherwise). For example, I want to create tables like this:
std::vector<double> vect1 (nmax), vect2 (nmax);
for (size_t i{0}; i < nmax; ++i) {
vect1[i] = f(i); // f is a routine defined somewhere else
vect2[i] = f(i)+2.0;
}
What I could use is the generate function with a lambda function and it would be something like this:
std::vector<double> vect1 (nmax), vect2 (nmax);
size_t count{0};
generate(vect1.begin(), vect1.end(), [&]() {return f(count++);});
count=0;
generate(vect2.begin(), vect2.end(), [&]() {return f(count++) + 2.0;});
But I’m not a big fan of that, because:
count exists until the end of the routine.
We see that with another function, I have to put back count to zero and generate another vector again. I have to track down all the count variables etc. With the for loop, I could just put it in the same loop.
With the for loop, the correspondence is seen easily. i is on the left and the right. With generate, I feel like it’s counting with a different variable on the left and the right, which means potential mistake.
I can only do count++, not ++count, which means copy of variables.
Of course, this is a simple example. But I would like to know if the generate() version is still better for this kind of things (code/performance/readability wise). Or maybe there’s a better way of doing it, and I’m open to all suggestions and comments.
Thanks!
I wrote an index range that lets me:
std::vector<double> vect1 (nmax), vect2 (nmax);
for (auto i : index_upto(nmax))
vect1[i] = f(i); // f is a routine defined somewhere else
vect2[i] = f(i)+2.0;
}
which eliminates the manual fenceposting but leaves the code otherwise unchanged.
This isn't all that hard. Write a pseudo-iterator that stores a T and returns a copy on unary *. It should support == and ++ (passing both into the stored T).
template<class T>
struct index_it {
T t;
index_it& operator++() { ++t; return *this; }
index_it operator++(int) { auto r = *this; ++*this; return r; }
friend bool operator==( index_it const& lhs, index_it const& rhs ) {
return lhs.t == rhs.t;
}
friend bool operator!=( index_it const& lhs, index_it const& rhs ) {
return lhs.t != rhs.t;
}
T operator*()const& { return t; }
T operator*()&& { return std::move(t); }
};
Next, write a range:
template<class It>
struct range {
It b, e;
It begin() const { return b; }
It end() const { return e; }
};
then compose the two.
template<class T>
using index_range = range<index_it<T>>;
template<class T>
index_range<T> make_index_range( T s, T f ) {
return {{std::move(s)}, {std::move(f)}};
}
index_range<std::size_t> index_upto( std::size_t n ) {
return make_index_range( std::size_t(0), n );
}
note that index_it is not an iterator, but works much like one. You could probably finish it and make it an input iterator; beyond that you run into problems as iterators expect backing containers.
Using a stateful lambda is not a good idea. You may be better off writing your own generate function that takes a function object receiving an iterator:
template<class ForwardIt, class Generator>
void generate_iter(ForwardIt first, ForwardIt last, Generator g) {
while (first != last) {
*first = g(first);
++first;
}
}
You can use it as follows:
generate_iter(vect1.begin(), vect1.end(), [&](const std::vector<double>::iterator& iter) {
auto count = std::distance(vect1.begin(), iter);
return f(count);
});
Demo.
We could use a mutable lambda...
#include <vector>
#include <algorithm>
double f(int x) { return x*2; }
int main()
{
constexpr int nmax = 100;
std::vector<double> vect1 (nmax), vect2 (nmax);
std::generate(vect1.begin(),
vect1.end(),
[count = int(0)]() mutable { return f(count++); });
std::generate(vect2.begin(),
vect2.end(),
[count = int(0)]() mutable { return f(count++) + 2.0; });
}
Another option (uses c++17 for template argument deduction):
template<class F>
struct counted_function
{
constexpr counted_function(F f, int start = 0, int step = 1)
: f(f)
, counter(start)
, step(step) {}
decltype(auto) operator()() {
return f(counter++);
}
F f;
int counter;
int step;
};
used as:
std::generate(vect2.begin(),
vect2.end(),
counted_function([](auto x) { return f(x) + 2.0; }));
And finally, just for fun, could write this:
generate(vect2).invoking(f).with(every<int>::from(0).to(nmax - 1));
...if we had written something like this...
#include <vector>
#include <algorithm>
#include <iterator>
double f(int x) { return x*2; }
template<class T> struct value_iter
{
using value_type = T;
using difference_type = T;
using reference = T&;
using pointer = T*;
using iterator_category = std::forward_iterator_tag;
friend bool operator==(value_iter l, value_iter r)
{
return l.current == r.current;
}
friend bool operator!=(value_iter l, value_iter r)
{
return !(l == r);
}
T const& operator*() const& { return current; }
value_iter& operator++() { ++current; return *this; }
T current;
};
template<class T> struct every
{
struct from_thing
{
T from;
struct to_thing
{
auto begin() const { return value_iter<T> { from };}
auto end() const { return value_iter<T> { to+1 };}
T from, to;
};
auto to(T x) { return to_thing { from, x }; }
};
static constexpr auto from(T start)
{
return from_thing { start };
}
};
template<class F>
struct counted_function
{
constexpr counted_function(F f, int start = 0, int step = 1)
: f(f)
, counter(start)
, step(step) {}
decltype(auto) operator()() {
return f(counter++);
}
F f;
int counter;
int step;
};
template <class Container> struct generate
{
generate(Container& c) : c(c) {}
template<class F>
struct invoking_thing
{
template<class Thing>
auto with(Thing thing)
{
using std::begin;
using std::end;
std::copy(begin(thing), end(thing), begin(c));
return c;
}
F f;
Container& c;
};
template<class F>
auto invoking(F f) { return invoking_thing<F>{f, c}; }
Container& c;
};
int main()
{
constexpr int nmax = 100;
std::vector<double> vect2 (nmax);
generate(vect2).invoking(f).with(every<int>::from(0).to(nmax - 1));
}
With range-v3, it would be something like:
auto vect1 = ranges::view::ints(0, nmax) | ranges::view::transform(f);
auto vect2 = ranges::view::ints(0, nmax) | ranges::view::transform(f2);
// or auto vect2 = vect1 | ranges::view::transform([](double d){ return d + 2.; });
Demo
Is there a better way to write:
for (auto i = container.begin(); i != container.end();)
{
if (condition(i))
{
i = container.erase(i);
continue;
}
++i;
}
This code does what I want, but it feels like bad style.
How can I improve it?
My container is std::map, but a generic solution would be cool.
Use erase + remove_if:
auto pred = /* lambda or something*/
container.erase(std::remove_if(container.begin(),
container.end(),
pred)
Is there a better way to...?
It is always subjective, but one way is a traits-based template function suite with a consistent interface, using tag-dispatching to choose the most optimal algorithm depending on the container type...
The interface function could look like this:
template<class Range, class Pred>
Range& erase_if(Range& range, Pred&& pred)
{
erase_if(typename detail::range_traits<std::decay_t<Range>>::idiom_type(),
range, std::forward<Pred>(pred));
return range;
}
which defers to the correct idiom for the container type...
void erase_if(erase_remove_idiom, Vector& vec, Pred pred)
{
vec.erase(std::remove_if(std::begin(vec), std::end(vec),
std::forward<Pred>(pred)),
std::end(vec));
}
template<class Maplike, class Pred>
void erase_if(equal_range_idiom, Maplike& map, Pred pred)
{
auto first = std::begin(map);
auto last = std::end(map);
while (first != last) {
auto& item = *first;
auto& key = get_key(item);
auto range = map.equal_range(key);
if (pred(key)) {
map.erase(range.first, range.second);
}
first = range.second;
}
}
template<class Maplike, class Pred>
void erase_if(map_crawl_idiom, Maplike& map, Pred pred)
{
for (auto i = map.begin(); i != map.end();)
{
i = pred(*i) ? map.erase(i) : std::next(i);
}
}
Here's the complete code and some tests.
Writing code like this always makes me feel admiration for std-library maintainers. So many corner cases...
#include <iostream>
#include <vector>
#include <algorithm>
#include <iterator>
#include <set>
#include <unordered_set>
#include <map>
namespace detail {
// The general concept of range_traits
template<class Range>
struct range_traits
{
};
// Tag for performing erase-remove on vector-like containers
struct erase_remove_idiom {};
// Using equal-range to skip redundant comparisons in multiset-like-containers
struct equal_range_idiom {};
// Crawling through maps...
struct map_crawl_idiom {};
template<class V, class A>
struct range_traits<std::vector<V, A>> {
using idiom_type = erase_remove_idiom;
};
template<class V, class C, class A>
struct range_traits<std::multiset<V, C, A>> {
using idiom_type = equal_range_idiom;
};
template<class V, class C, class A>
struct range_traits<std::set<V, C, A>> {
using idiom_type = map_crawl_idiom;
};
template<class V, class C, class H, class A>
struct range_traits<std::unordered_set<V, C, H, A>> {
using idiom_type = map_crawl_idiom;
};
template<class V, class C, class H, class A>
struct range_traits<std::unordered_multiset<V, C, H, A>> {
using idiom_type = equal_range_idiom;
};
template<class K, class V, class C, class A>
struct range_traits<std::multimap<K, V, C, A>> {
using idiom_type = map_crawl_idiom;
};
template<class K, class V, class C, class A>
struct range_traits<std::map<K, V, C, A>> {
using idiom_type = map_crawl_idiom;
};
}
namespace detail {
template<class Vector, class Pred>
void erase_if(erase_remove_idiom, Vector& vec, Pred pred)
{
vec.erase(std::remove_if(std::begin(vec), std::end(vec),
std::forward<Pred>(pred)),
std::end(vec));
}
// Generalised key-getter for sets
template<class V>
V& get_key(V& v) {
return v;
}
// Specialised key-getter for maps
template<class K, class V>
const K& get_key(std::pair<const K, V>& p) {
return p.first;
}
template<class Maplike, class Pred>
void erase_if(equal_range_idiom, Maplike& map, Pred pred)
{
auto first = std::begin(map);
auto last = std::end(map);
while (first != last) {
auto& item = *first;
auto& key = get_key(item);
auto range = map.equal_range(key);
if (pred(key)) {
map.erase(range.first, range.second);
}
first = range.second;
}
}
template<class Maplike, class Pred>
void erase_if(map_crawl_idiom, Maplike& map, Pred pred)
{
for (auto i = map.begin(); i != map.end();)
{
i = pred(*i) ? map.erase(i) : std::next(i);
}
}
}
//
// The interface function
//
template<class Range, class Pred>
Range& erase_if(Range& range, Pred&& pred)
{
erase_if(typename detail::range_traits<std::decay_t<Range>>::idiom_type(),
range, std::forward<Pred>(pred));
return range;
}
template<class T>
struct emitter
{
void operator()(std::ostream& os, const T& t) const {
os << t;
}
};
template<class K, class V>
struct emitter<std::pair<const K, V>>
{
void operator()(std::ostream& os, const std::pair<const K, V>& p) const {
os << "(" << p.first << ", " << p.second << ")";
}
};
template<class Range>
void dump(Range& range)
{
auto sep = "";
auto e = emitter<typename std::decay_t<Range>::value_type> {};
for (auto& item : range) {
std::cout << sep;
e(std::cout, item);
sep = ", ";
}
std::cout << std::endl;
}
//
// Test on various containers
//
int main()
{
std::vector<int> some { 1,0,1,0,1,0,1,0 };
std::multiset<int> some_mset { 1,0,1,0,1,0,1,0 };
std::unordered_multiset<int> some_umset { some_mset.begin(), some_mset.end() };
std::map<int, std::string> some_map {
{ 1, "mouse" },
{ 2, "house" },
{ 3, "mouth" }
};
std::multimap<int, std::string> some_mmap(some_map.begin(), some_map.end());
some_mmap.insert(some_map.begin(), some_map.end());
std::set<int> some_set { 0, 1, 2, 3, 4, 5, 6, 7, 8, 9 };
auto is_one = [](auto& x) { return x == 1; };
auto is_even = [](auto&x) { return (x % 2) == 0; };
auto value_starts_with_m = [](auto& item) { return item.second.substr(0,1) == "m"; };
dump(erase_if(some, is_one));
dump(erase_if(some_mset, is_one));
dump(erase_if(some_umset, is_one));
dump(erase_if(some_map, value_starts_with_m));
dump(erase_if(some_mmap, value_starts_with_m));
dump(erase_if(some_set, is_even));
}
Expected results:
0, 0, 0, 0
0, 0, 0, 0
0, 0, 0, 0
(2, house)
(2, house), (2, house)
1, 3, 5, 7, 9
All of these questions and comments are quaint, but they don't address the original question.
What was asked is, how does one tighten up the following:
for( i = ...; some_condition( i ); ) {
if( another_condition( i ) ) {
erase( i );
continue;
}
i++;
}
And the answer is:
for( i = ...; some_condition( i ); i++ ) {
if( another_condition( i ) ) {
erase( i );
}
}
With the new range-based for-loop we can write code like:
for(auto x: Y) {}
Which IMO is a huge improvement from (for ex.)
for(std::vector<int>::iterator x=Y.begin(); x!=Y.end(); ++x) {}
Can it be used to loop over two simultaneous loops, like Python's zip function? For those unfamiliar with Python, the code:
Y1 = [1, 2, 3]
Y2 = [4, 5, 6, 7]
for x1,x2 in zip(Y1, Y2):
print(x1, x2)
Gives as output (1,4) (2,5) (3,6)
Warning: boost::zip_iterator and boost::combine as of Boost 1.63.0 (2016 Dec 26) will cause undefined behavior if the length of the input containers are not the same (it may crash or iterate beyond the end).
Starting from Boost 1.56.0 (2014 Aug 7) you could use boost::combine (the function exists in earlier versions but undocumented):
#include <boost/range/combine.hpp>
#include <vector>
#include <list>
#include <string>
int main() {
std::vector<int> a {4, 5, 6};
double b[] = {7, 8, 9};
std::list<std::string> c {"a", "b", "c"};
for (auto tup : boost::combine(a, b, c, a)) { // <---
int x, w;
double y;
std::string z;
boost::tie(x, y, z, w) = tup;
printf("%d %g %s %d\n", x, y, z.c_str(), w);
}
}
This would print
4 7 a 4
5 8 b 5
6 9 c 6
In earlier versions, you could define a range yourself like this:
#include <boost/iterator/zip_iterator.hpp>
#include <boost/range.hpp>
template <typename... T>
auto zip(T&&... containers) -> boost::iterator_range<boost::zip_iterator<decltype(boost::make_tuple(std::begin(containers)...))>>
{
auto zip_begin = boost::make_zip_iterator(boost::make_tuple(std::begin(containers)...));
auto zip_end = boost::make_zip_iterator(boost::make_tuple(std::end(containers)...));
return boost::make_iterator_range(zip_begin, zip_end);
}
The usage is the same.
std::transform can do this trivially:
std::vector<int> a = {1,2,3,4,5};
std::vector<int> b = {1,2,3,4,5};
std::vector<int>c;
std::transform(a.begin(),a.end(), b.begin(),
std::back_inserter(c),
[](const auto& aa, const auto& bb)
{
return aa*bb;
});
for(auto cc:c)
std::cout<<cc<<std::endl;
If the second sequence is shorter, my implementation seems to be giving default initialized values.
So I wrote this zip before when I was bored, I decided to post it because it's different than the others in that it doesn't use boost and looks more like the c++ stdlib.
template <typename Iterator>
void advance_all (Iterator & iterator) {
++iterator;
}
template <typename Iterator, typename ... Iterators>
void advance_all (Iterator & iterator, Iterators& ... iterators) {
++iterator;
advance_all(iterators...);
}
template <typename Function, typename Iterator, typename ... Iterators>
Function zip (Function func, Iterator begin,
Iterator end,
Iterators ... iterators)
{
for(;begin != end; ++begin, advance_all(iterators...))
func(*begin, *(iterators)... );
//could also make this a tuple
return func;
}
Example use:
int main () {
std::vector<int> v1{1,2,3};
std::vector<int> v2{3,2,1};
std::vector<float> v3{1.2,2.4,9.0};
std::vector<float> v4{1.2,2.4,9.0};
zip (
[](int i,int j,float k,float l){
std::cout << i << " " << j << " " << k << " " << l << std::endl;
},
v1.begin(),v1.end(),v2.begin(),v3.begin(),v4.begin());
}
With range-v3:
#include <range/v3/all.hpp>
#include <vector>
#include <iostream>
namespace ranges {
template <class T, class U>
std::ostream& operator << (std::ostream& os, common_pair<T, U> const& p)
{
return os << '(' << p.first << ", " << p.second << ')';
}
}
using namespace ranges::v3;
int main()
{
std::vector<int> a {4, 5, 6};
double b[] = {7, 8, 9};
std::cout << view::zip(a, b) << std::endl;
}
The output:
[(4, 7),(5, 8),(6, 9)]
See <redi/zip.h> for a zip function which works with range-base for and accepts any number of ranges, which can be rvalues or lvalues and can be different lengths (iteration will stop at the end of the shortest range).
std::vector<int> vi{ 0, 2, 4 };
std::vector<std::string> vs{ "1", "3", "5", "7" };
for (auto i : redi::zip(vi, vs))
std::cout << i.get<0>() << ' ' << i.get<1>() << ' ';
Prints 0 1 2 3 4 5
You can use a solution based on boost::zip_iterator. Make a phony container class maintaining references to your containers, and which return zip_iterator from the begin and end member functions. Now you can write
for (auto p: zip(c1, c2)) { ... }
Example implementation (please test):
#include <iterator>
#include <boost/iterator/zip_iterator.hpp>
template <typename C1, typename C2>
class zip_container
{
C1* c1; C2* c2;
typedef boost::tuple<
decltype(std::begin(*c1)),
decltype(std::begin(*c2))
> tuple;
public:
zip_container(C1& c1, C2& c2) : c1(&c1), c2(&c2) {}
typedef boost::zip_iterator<tuple> iterator;
iterator begin() const
{
return iterator(std::begin(*c1), std::begin(*c2));
}
iterator end() const
{
return iterator(std::end(*c1), std::end(*c2));
}
};
template <typename C1, typename C2>
zip_container<C1, C2> zip(C1& c1, C2& c2)
{
return zip_container<C1, C2>(c1, c2);
}
I leave the variadic version as an excellent exercise to the reader.
If you like operator overloading, here are three possibilities. The first two are using std::pair<> and std::tuple<>, respectively, as iterators; the third extends this to range-based for. Note that not everyone will like these definitions of the operators, so it's best to keep them in a separate namespace and have a using namespace in the functions (not files!) where you'd like to use these.
#include <iostream>
#include <utility>
#include <vector>
#include <tuple>
// put these in namespaces so we don't pollute global
namespace pair_iterators
{
template<typename T1, typename T2>
std::pair<T1, T2> operator++(std::pair<T1, T2>& it)
{
++it.first;
++it.second;
return it;
}
}
namespace tuple_iterators
{
// you might want to make this generic (via param pack)
template<typename T1, typename T2, typename T3>
auto operator++(std::tuple<T1, T2, T3>& it)
{
++( std::get<0>( it ) );
++( std::get<1>( it ) );
++( std::get<2>( it ) );
return it;
}
template<typename T1, typename T2, typename T3>
auto operator*(const std::tuple<T1, T2, T3>& it)
{
return std::tie( *( std::get<0>( it ) ),
*( std::get<1>( it ) ),
*( std::get<2>( it ) ) );
}
// needed due to ADL-only lookup
template<typename... Args>
struct tuple_c
{
std::tuple<Args...> containers;
};
template<typename... Args>
auto tie_c( const Args&... args )
{
tuple_c<Args...> ret = { std::tie(args...) };
return ret;
}
template<typename T1, typename T2, typename T3>
auto begin( const tuple_c<T1, T2, T3>& c )
{
return std::make_tuple( std::get<0>( c.containers ).begin(),
std::get<1>( c.containers ).begin(),
std::get<2>( c.containers ).begin() );
}
template<typename T1, typename T2, typename T3>
auto end( const tuple_c<T1, T2, T3>& c )
{
return std::make_tuple( std::get<0>( c.containers ).end(),
std::get<1>( c.containers ).end(),
std::get<2>( c.containers ).end() );
}
// implement cbegin(), cend() as needed
}
int main()
{
using namespace pair_iterators;
using namespace tuple_iterators;
std::vector<double> ds = { 0.0, 0.1, 0.2 };
std::vector<int > is = { 1, 2, 3 };
std::vector<char > cs = { 'a', 'b', 'c' };
// classical, iterator-style using pairs
for( auto its = std::make_pair(ds.begin(), is.begin()),
end = std::make_pair(ds.end(), is.end() ); its != end; ++its )
{
std::cout << "1. " << *(its.first ) + *(its.second) << " " << std::endl;
}
// classical, iterator-style using tuples
for( auto its = std::make_tuple(ds.begin(), is.begin(), cs.begin()),
end = std::make_tuple(ds.end(), is.end(), cs.end() ); its != end; ++its )
{
std::cout << "2. " << *(std::get<0>(its)) + *(std::get<1>(its)) << " "
<< *(std::get<2>(its)) << " " << std::endl;
}
// range for using tuples
for( const auto& d_i_c : tie_c( ds, is, cs ) )
{
std::cout << "3. " << std::get<0>(d_i_c) + std::get<1>(d_i_c) << " "
<< std::get<2>(d_i_c) << " " << std::endl;
}
}
// declare a, b
BOOST_FOREACH(boost::tie(a, b), boost::combine(list_of_a, list_of_b)){
// your code here.
}
I ran into this same question independently and didn't like the syntax of any of the above. So, I have a short header file that essentially does the same as the boost zip_iterator but has a few macros to make the syntax more palatable to me:
https://github.com/cshelton/zipfor
For example you can do
vector<int> a {1,2,3};
array<string,3> b {"hello","there","coders"};
zipfor(i,s eachin a,b)
cout << i << " => " << s << endl;
The main syntactic sugar is that I can name the elements from each container. I also include a "mapfor" that does the same, but for maps (to name the ".first" and ".second" of the element).
If you have a C++14 compliant compiler (e.g. gcc5) you can use zip provided in the cppitertools library by Ryan Haining, which looks really promising:
array<int,4> i{{1,2,3,4}};
vector<float> f{1.2,1.4,12.3,4.5,9.9};
vector<string> s{"i","like","apples","alot","dude"};
array<double,5> d{{1.2,1.2,1.2,1.2,1.2}};
for (auto&& e : zip(i,f,s,d)) {
cout << std::get<0>(e) << ' '
<< std::get<1>(e) << ' '
<< std::get<2>(e) << ' '
<< std::get<3>(e) << '\n';
std::get<1>(e)=2.2f; // modifies the underlying 'f' array
}
For a C++ stream processing library I'm writing I was looking for a solution that doesn't rely on third party libraries and works with an arbitrary number of containers. I ended up with this solution. It's similar to the accepted solution which uses boost (and also results in undefined behavior if the container lengths are not equal)
#include <utility>
namespace impl {
template <typename Iter, typename... Iters>
class zip_iterator {
public:
using value_type = std::tuple<const typename Iter::value_type&,
const typename Iters::value_type&...>;
zip_iterator(const Iter &head, const Iters&... tail)
: head_(head), tail_(tail...) { }
value_type operator*() const {
return std::tuple_cat(std::tuple<const typename Iter::value_type&>(*head_), *tail_);
}
zip_iterator& operator++() {
++head_; ++tail_;
return *this;
}
bool operator==(const zip_iterator &rhs) const {
return head_ == rhs.head_ && tail_ == rhs.tail_;
}
bool operator!=(const zip_iterator &rhs) const {
return !(*this == rhs);
}
private:
Iter head_;
zip_iterator<Iters...> tail_;
};
template <typename Iter>
class zip_iterator<Iter> {
public:
using value_type = std::tuple<const typename Iter::value_type&>;
zip_iterator(const Iter &head) : head_(head) { }
value_type operator*() const {
return value_type(*head_);
}
zip_iterator& operator++() { ++head_; return *this; }
bool operator==(const zip_iterator &rhs) const { return head_ == rhs.head_; }
bool operator!=(const zip_iterator &rhs) const { return !(*this == rhs); }
private:
Iter head_;
};
} // namespace impl
template <typename Iter>
class seq {
public:
using iterator = Iter;
seq(const Iter &begin, const Iter &end) : begin_(begin), end_(end) { }
iterator begin() const { return begin_; }
iterator end() const { return end_; }
private:
Iter begin_, end_;
};
/* WARNING: Undefined behavior if iterator lengths are different.
*/
template <typename... Seqs>
seq<impl::zip_iterator<typename Seqs::iterator...>>
zip(const Seqs&... seqs) {
return seq<impl::zip_iterator<typename Seqs::iterator...>>(
impl::zip_iterator<typename Seqs::iterator...>(std::begin(seqs)...),
impl::zip_iterator<typename Seqs::iterator...>(std::end(seqs)...));
}
From C++23, we can iterate on std::views::zip.
Below is simple example.
#include <iostream>
#include <ranges>
#include <vector>
int main() {
std::vector<int> x {4, 5, 6};
double y[] = {7, 8, 9};
for (auto [elem1,elem2] : std::views::zip(x, y))
std::cout << "[" << elem1 << "," << elem2 << "]" << " ";
}
The output can be verified below (an online compiler). Not sure how many days the link exists.
https://godbolt.org/z/KjjE4eeGY
An improvement on aaronman's solution:
Still C++11.
No recursive template expansion.
Support for container zipping.
Utilizes the approach of Sean Parent's famed for_each_arg().
// Includes only required for the example main() below!
#include <vector>
#include <iostream>
namespace detail {
struct advance {
template <typename T> void operator()(T& t) const { ++t; }
};
// Adaptation of for_each_arg, see:
// https://isocpp.org/blog/2015/01/for-each-argument-sean-parent
template <class... Iterators>
void advance_all(Iterators&... iterators) {
[](...){}((advance{}(iterators), 0)...);
}
} // namespace detail
template <typename F, typename Iterator, typename ... ExtraIterators>
F for_each_zipped(
F func,
Iterator begin,
Iterator end,
ExtraIterators ... extra_iterators)
{
for(;begin != end; ++begin, detail::advance_all(extra_iterators...))
func(*begin, *(extra_iterators)... );
return func;
}
template <typename F, typename Container, typename... ExtraContainers>
F for_each_zipped_containers(
F func,
Container& container,
ExtraContainers& ... extra_containers)
{
return for_each_zipped(
func, std::begin(container), std::end(container), std::begin(extra_containers)...);
}
int main () {
std::vector<int> v1 { 1, 2, 3};
std::vector<int> v2 { 3, 2, 1};
std::vector<float> v3 {1.2, 2.4, 9.0};
std::vector<float> v4 {1.2, 2.4, 9.0};
auto print_quartet =
[](int i,int j,float k,float l) {
std::cout << i << " " << j << " " << k << " " << l << '\n';
};
std::cout << "Using zipped iterators:\n";
for_each_zipped(print_quartet, v1.begin(), v1.end(), v2.begin(), v3.begin(), v4.begin());
std::cout << "\nUsing zipped containers:\n";
for_each_zipped_containers(print_quartet, v1, v2, v3, v4);
}
See it working on GodBolt.
I would propose this one. I found it to be quite elegant, and exactly what I (and you) needed.
https://github.com/CommitThis/zip-iterator
Just in case here's a code copy. Note, it is distributed under MIT License, also don't forget to put name of author.
zip.hpp
/***
* MIT License
* Author: G Davey
*/
#pragma once
#include <cassert>
#include <functional>
#include <iomanip>
#include <iostream>
#include <list>
#include <string>
#include <vector>
#include <typeinfo>
namespace c9 {
template <typename Iter>
using select_access_type_for = std::conditional_t<
std::is_same_v<Iter, std::vector<bool>::iterator> ||
std::is_same_v<Iter, std::vector<bool>::const_iterator>,
typename Iter::value_type,
typename Iter::reference
>;
template <typename ... Args, std::size_t ... Index>
auto any_match_impl(std::tuple<Args...> const & lhs,
std::tuple<Args...> const & rhs,
std::index_sequence<Index...>) -> bool
{
auto result = false;
result = (... | (std::get<Index>(lhs) == std::get<Index>(rhs)));
return result;
}
template <typename ... Args>
auto any_match(std::tuple<Args...> const & lhs, std::tuple<Args...> const & rhs)
-> bool
{
return any_match_impl(lhs, rhs, std::index_sequence_for<Args...>{});
}
template <typename ... Iters>
class zip_iterator
{
public:
using value_type = std::tuple<
select_access_type_for<Iters>...
>;
zip_iterator() = delete;
zip_iterator(Iters && ... iters)
: m_iters {std::forward<Iters>(iters)...}
{
}
auto operator++() -> zip_iterator&
{
std::apply([](auto && ... args){ ((args += 1), ...); }, m_iters);
return *this;
}
auto operator++(int) -> zip_iterator
{
auto tmp = *this;
++*this;
return tmp;
}
auto operator!=(zip_iterator const & other)
{
return !(*this == other);
}
auto operator==(zip_iterator const & other)
{
auto result = false;
return any_match(m_iters, other.m_iters);
}
auto operator*() -> value_type
{
return std::apply([](auto && ... args){
return value_type(*args...);
}, m_iters);
}
private:
std::tuple<Iters...> m_iters;
};
/* std::decay needed because T is a reference, and is not a complete type */
template <typename T>
using select_iterator_for = std::conditional_t<
std::is_const_v<std::remove_reference_t<T>>,
typename std::decay_t<T>::const_iterator,
typename std::decay_t<T>::iterator>;
template <typename ... T>
class zipper
{
public:
using zip_type = zip_iterator<select_iterator_for<T> ...>;
template <typename ... Args>
zipper(Args && ... args)
: m_args{std::forward<Args>(args)...}
{
}
auto begin() -> zip_type
{
return std::apply([](auto && ... args){
return zip_type(std::begin(args)...);
}, m_args);
}
auto end() -> zip_type
{
return std::apply([](auto && ... args){
return zip_type(std::end(args)...);
}, m_args);
}
private:
std::tuple<T ...> m_args;
};
template <typename ... T>
auto zip(T && ... t)
{
return zipper<T ...>{std::forward<T>(t)...};
}
}
Example
#include "zip.hpp"
#include <vector>
std::vector<int> a, b, c;
void foo() {
for (auto && [x, y] : zip(a, b))
c.push_back(x + z);
}
Boost.Iterators has zip_iterator you can use (example's in the docs). It won't work with range for, but you can use std::for_each and a lambda.
Here is a simple version that does not require boost. It won't be particularly efficient as it creates temporary values, and it does not generalise over containers other than lists, but it has no dependencies and it solves the most common case for zipping.
template<class L, class R>
std::list< std::pair<L,R> > zip(std::list<L> left, std::list<R> right)
{
auto l = left.begin();
auto r = right.begin();
std::list< std::pair<L,R> > result;
while( l!=left.end() && r!=right.end() )
result.push_back( std::pair<L,R>( *(l++), *(r++) ) );
return result;
}
Although the other versions are more flexible, often the point of using a list operator is make a simple one-liner. This version has the benefit that the common-case is simple.
I wanted to replace the loop with an algorithm in the following code
int numbers[] = { ... };
vector<int> output;
for( int* it = numbers+from; it != numbers+to ; ++it )
{
int square = func( *it );
if( predicate(square) )
{
output.push_back(square);
}
}
The program is meant to transform the values and copy them to a destination if a condition occurs.
I could not use std::copy_if because that would not apply a transformation.
I could not use std::transform because that lacks a predicate
It is not even a good idea to write a transform_copy_if() , because of the intermediate copy of the transformed variable.
It looks like my only hope is to create a conditional_back_insert_iterator. Then I could have a pretty decent call like:
int numbers[] = { ... };
vector<int> output;
std::transform(numbers+from, numbers+to,
conditional_back_inserter(predicate, output),
func);
Is this solution the best way to treat such cases ? I couldn't even google for conditional inserters, so I am worried I'm on the wrong path.
I could also imagine that I could implement an alternative solution such as
std::copy_if( transform_iterator<func>(numbers+from),
transform_iterator<func>(numbers+to),
back_inserter(output) );
(which reminds me of an example of *filter_iterators* in boost)
but that does not offer readability.
I think creating your own iterator is the way to go:
#include <iostream>
#include <vector>
#include <iterator>
#include <functional>
template<class T>
class conditional_back_insert_iterator
: public std::back_insert_iterator<std::vector<T>>
{
private:
using Base = std::back_insert_iterator<std::vector<T>>;
using Container = std::vector<T>;
using value_type = typename Container::value_type;
public:
template<class F>
conditional_back_insert_iterator(Container& other, F&& pred)
: Base(other), c(other), predicate(std::forward<F>(pred))
{ }
conditional_back_insert_iterator<T>& operator*()
{ return *this; }
conditional_back_insert_iterator<T>&
operator=(const value_type& val) const
{
if (predicate(val))
c.push_back(val);
return *this;
}
conditional_back_insert_iterator<T>&
operator=(value_type&& val) const
{
if (predicate(val))
c.push_back(std::move(val));
return *this;
}
private:
Container& c;
std::function<bool (const value_type&)> predicate;
};
template<
class Container,
class F,
class value_type = typename Container::value_type
>
conditional_back_insert_iterator<value_type>
conditional_back_inserter(Container& c, F&& predicate)
{
return conditional_back_insert_iterator<value_type>(c, std::forward<F>(predicate));
}
int main()
{
std::vector<int> v{1, 2, 3, 4, 5, 6, 7, 8, 9};
std::vector<int> to;
auto is_even = [] (int x) { return (x % 2) == 0; };
std::copy(v.begin(), v.end(), conditional_back_inserter(to, is_even));
}
Here's my attempt.
#include <algorithm>
#include <iostream>
#include <iterator>
#include <vector>
template <class Container, class Pred>
class conditional_insert_iterator
: public std::iterator< std::output_iterator_tag, void, void, void, void >
{
public:
explicit conditional_insert_iterator(Container& c, Pred p) : container(&c), pred(p) {}
conditional_insert_iterator& operator=(typename Container::const_reference value) {
if (pred(value))
container->push_back(value);
return *this;
}
conditional_insert_iterator& operator*() {return *this;}
conditional_insert_iterator& operator++() {return *this;}
conditional_insert_iterator& operator++(int) {return *this;}
private:
Container* container;
Pred pred;
};
template< class Container, class Pred>
conditional_insert_iterator<Container, Pred> conditional_inserter( Container& c, Pred pred )
{
return conditional_insert_iterator<Container, Pred>(c, pred);
}
using namespace std;
int main()
{
vector<int> in = { 1, 2, 3, 4, 5, 6 };
vector<int> out;
transform(in.begin(), in.end(),
conditional_inserter(out, [](int i) { return i%2 == 0;}),
[](int i) { return i + 2;});
for (auto i : out)
cout << i << "\n";
return 0;
}