C++ 同时迭代两个或多个容器的最佳方法是什么
声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow
原文地址: http://stackoverflow.com/questions/12552277/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me):
StackOverFlow
What's the best way to iterate over two or more containers simultaneously
提问by memecs
C++11 provides multiple ways to iterate over containers. For example:
C++11 提供了多种迭代容器的方法。例如:
Range-based loop
基于范围的循环
for(auto c : container) fun(c)
std::for_each
std::for_each
for_each(container.begin(),container.end(),fun)
However what is the recommended way to iterate over two (or more) containers of the same size to accomplish something like:
但是,迭代两个(或更多)相同大小的容器以完成以下操作的推荐方法是什么:
for(unsigned i = 0; i < containerA.size(); ++i) {
containerA[i] = containerB[i];
}
回答by Konrad Rudolph
Rather late to the party. But: I would iterate over indices. But not with the classical for
loop but instead with a range-based for
loop over the indices:
参加晚会比较晚。但是:我会遍历索引。但不是使用经典for
循环,而是使用基于范围for
的索引循环:
for(unsigned i : indices(containerA)) {
containerA[i] = containerB[i];
}
indices
is a simple wrapper function which returns a (lazily evaluated) range for the indices. Since the implementation – though simple –?is a bit too long to post it here, you can find an implementation on GitHub.
indices
是一个简单的包装函数,它返回索引的(延迟计算的)范围。由于实现——虽然简单——有点太长,无法在此处发布,您可以在 GitHub 上找到实现。
This code is as efficientas using a manual, classical for
loop.
此代码与使用手动经典循环一样有效for
。
If this pattern occurs often in your data, consider using another pattern which zip
s two sequences and produces a range of tuples, corresponding to the paired elements:
如果此模式经常出现在您的数据中,请考虑使用另一种模式,该模式包含zip
两个序列并生成一系列元组,对应于成对元素:
for (auto& [a, b] : zip(containerA, containerB)) {
a = b;
}
The implementation of zip
is left as an exercise for the reader, but it follows easily from the implementation of indices
.
的实现zip
留给读者作为练习,但它很容易从 的实现中得出indices
。
(Before C++17 you'd have to write the following instead:)
(在 C++17 之前,您必须改为编写以下内容:)
for (auto items&& : zip(containerA, containerB))
get<0>(items) = get<1>(items);
回答by Xeo
For your specific example, just use
对于您的具体示例,只需使用
std::copy_n(contB.begin(), contA.size(), contA.begin())
For the more general case, you can use Boost.Iterator's zip_iterator
, with a small function to make it usable in range-based for loops. For most cases, this will work:
对于更一般的情况,您可以使用 Boost.Iterator'szip_iterator
和一个小函数,使其可用于基于范围的 for 循环。在大多数情况下,这将起作用:
template<class... Conts>
auto zip_range(Conts&... conts)
-> decltype(boost::make_iterator_range(
boost::make_zip_iterator(boost::make_tuple(conts.begin()...)),
boost::make_zip_iterator(boost::make_tuple(conts.end()...))))
{
return {boost::make_zip_iterator(boost::make_tuple(conts.begin()...)),
boost::make_zip_iterator(boost::make_tuple(conts.end()...))};
}
// ...
for(auto&& t : zip_range(contA, contB))
std::cout << t.get<0>() << " : " << t.get<1>() << "\n";
However, for full-blown genericity, you probably want something more like this, which will work correctly for arrays and user-defined types that don't have member begin()
/end()
but dohave begin
/end
functions in their namespace. Also, this will allow the user to specifically get const
access through the zip_c...
functions.
然而,对于全面的通用性,你可能想要的东西更像是这样,这将正常工作,数组和用户定义的类型没有成员begin()
/end()
但也有begin
/end
在他们的命名空间功能。此外,这将允许用户专门const
访问这些zip_c...
功能。
And if you're an advocate of nice error messages, like me, then you probably want this, which checks if any temporary containers were passed to any of the zip_...
functions, and prints a nice error message if so.
如果你像我一样支持良好的错误消息,那么你可能想要this,它检查是否有任何临时容器被传递给任何zip_...
函数,如果是,则打印一条不错的错误消息。
回答by Joseph
i wonder why no one mentioned this:
我想知道为什么没有人提到这一点:
auto ItA = VectorA.begin();
auto ItB = VectorB.begin();
while(ItA != VectorA.end() || ItB != VectorB.end())
{
if(ItA != VectorA.end())
{
++ItA;
}
if(ItB != VectorB.end())
{
++ItB;
}
}
PS: if the container sizes don't match,then you will have to put the code inside the if statements.
PS:如果容器大小不匹配,则必须将代码放在 if 语句中。
回答by wjl
There are a lot of ways to do specific thingswith multiple containers as provided in the algorithm
header. For instance, in the example you've given, you could use std::copy
instead of an explicit for loop.
有很多方法可以使用标头中提供的多个容器执行特定操作algorithm
。例如,在您给出的示例中,您可以使用std::copy
代替显式 for 循环。
On the other hand, there isn't any built-in way to generically iterate multiple containers other than a normal for loop. This isn't surprising because there are a lotof ways to iterate. Think about it: you could iterate through one container with one step, one container with another step; or through one container until it gets to the end then start inserting while you go through to the end of the other container; or one step of the first container for every time you completely go through the other container then start over; or some other pattern; or more than two containers at a time; etc ...
另一方面,除了普通的 for 循环之外,没有任何内置的方法来一般地迭代多个容器。这并不奇怪,因为有很多迭代方法。想一想:你可以一步迭代一个容器,一个容器再一步;或者通过一个容器直到它到达最后然后开始插入,同时你通过另一个容器的末端;或每次您完全通过另一个容器时第一个容器的一步,然后重新开始;或其他一些模式;或一次超过两个容器;等等 ...
However, if you wanted to make your own"for_each" style function that iterates through two containers only up to the length of the shortest one, you could do something like this:
但是,如果您想创建自己的“for_each”样式函数,该函数仅迭代两个容器,直至达到最短容器的长度,您可以执行以下操作:
template <typename Container1, typename Container2>
void custom_for_each(
Container1 &c1,
Container2 &c2,
std::function<void(Container1::iterator &it1, Container2::iterator &it2)> f)
{
Container1::iterator begin1 = c1.begin();
Container2::iterator begin2 = c2.begin();
Container1::iterator end1 = c1.end();
Container2::iterator end2 = c2.end();
Container1::iterator i1;
Container1::iterator i2;
for (i1 = begin1, i2 = begin2; (i1 != end1) && (i2 != end2); ++it1, ++i2) {
f(i1, i2);
}
}
Obviously you can make any kind of iterations strategy you want in a similar way.
显然,您可以以类似的方式制定您想要的任何类型的迭代策略。
Of course, you might argue that just doing the inner for loop directly is easier than writing a custom function like this ... and you'd be right, if you are only going to do it one or two times. But the nice thing is that this is very reusable. =)
当然,您可能会争辩说,直接执行内部 for 循环比编写这样的自定义函数更容易……如果您只打算执行一两次,那您是对的。但好消息是,这是非常可重用的。=)
回答by czarles
In case when you need to iterate simultaneously over 2 containers only, there is an extended version of standard for_each algorithm in boost range library, e.g:
如果您只需要同时迭代 2 个容器,则 boost 范围库中有一个标准 for_each 算法的扩展版本,例如:
#include <vector>
#include <boost/assign/list_of.hpp>
#include <boost/bind.hpp>
#include <boost/range/algorithm_ext/for_each.hpp>
void foo(int a, int& b)
{
b = a + 1;
}
int main()
{
std::vector<int> contA = boost::assign::list_of(4)(3)(5)(2);
std::vector<int> contB(contA.size(), 0);
boost::for_each(contA, contB, boost::bind(&foo, _1, _2));
// contB will be now 5,4,6,3
//...
return 0;
}
When you need to handle more than 2 containers in one algorithm, then you need to play with zip.
当您需要在一种算法中处理 2 个以上的容器时,您需要使用 zip。
回答by Vahid
another solution could be capturing a reference of the iterator of the other container in a lambda and using post increment operator on that. for example simple copy would be:
另一种解决方案可能是在 lambda 中捕获另一个容器的迭代器的引用,并在其上使用后增量运算符。例如简单的副本将是:
vector<double> a{1, 2, 3};
vector<double> b(3);
auto ita = a.begin();
for_each(b.begin(), b.end(), [&ita](auto &itb) { itb = *ita++; })
inside lambda you can do whatever with ita
and then increment it. This easily extends to the multiple containers case.
在 lambda 中,你可以做任何事情,ita
然后增加它。这很容易扩展到多个容器的情况。
回答by Jens
A range-library provides this and other very helpful functionality. The following example uses Boost.Range. Eric Niebler's rangev3should be a good alternative.
范围库提供了这个和其他非常有用的功能。以下示例使用Boost.Range。Eric Niebler 的 rangev3应该是一个不错的选择。
#include <boost/range/combine.hpp>
#include <iostream>
#include <vector>
#include <list>
int main(int, const char*[])
{
std::vector<int> const v{0,1,2,3,4};
std::list<char> const l{'a', 'b', 'c', 'd', 'e'};
for(auto const& i: boost::combine(v, l))
{
int ti;
char tc;
boost::tie(ti,tc) = i;
std::cout << '(' << ti << ',' << tc << ')' << '\n';
}
return 0;
}
C++17 will make this even better with structured bindings:
C++17 将通过结构化绑定使这更好:
int main(int, const char*[])
{
std::vector<int> const v{0,1,2,3,4};
std::list<char> const l{'a', 'b', 'c', 'd', 'e'};
for(auto const& [ti, tc]: boost::combine(v, l))
{
std::cout << '(' << ti << ',' << tc << ')' << '\n';
}
return 0;
}
回答by Szymon Marczak
I'm a bit late too; but you can use this (C-style variadic function):
我也有点晚了;但你可以使用这个(C 风格的可变参数函数):
template<typename T>
void foreach(std::function<void(T)> callback, int count, ...) {
va_list args;
va_start(args, count);
for (int i = 0; i < count; i++) {
std::vector<T> v = va_arg(args, std::vector<T>);
std::for_each(v.begin(), v.end(), callback);
}
va_end(args);
}
foreach<int>([](const int &i) {
// do something here
}, 6, vecA, vecB, vecC, vecD, vecE, vecF);
or this (using a function parameter pack):
或者这个(使用函数参数包):
template<typename Func, typename T>
void foreach(Func callback, std::vector<T> &v) {
std::for_each(v.begin(), v.end(), callback);
}
template<typename Func, typename T, typename... Args>
void foreach(Func callback, std::vector<T> &v, Args... args) {
std::for_each(v.begin(), v.end(), callback);
return foreach(callback, args...);
}
foreach([](const int &i){
// do something here
}, vecA, vecB, vecC, vecD, vecE, vecF);
or this (using a brace-enclosed initializer list):
或者这个(使用大括号括起来的初始化列表):
template<typename Func, typename T>
void foreach(Func callback, std::initializer_list<std::vector<T>> list) {
for (auto &vec : list) {
std::for_each(vec.begin(), vec.end(), callback);
}
}
foreach([](const int &i){
// do something here
}, {vecA, vecB, vecC, vecD, vecE, vecF});
or you can join vectors like here: What is the best way to concatenate two vectors?and then iterate over big vector.
或者你可以像这里这样加入向量:连接两个向量的最佳方法是什么?然后迭代大向量。
回答by user877329
Here is one variant
这是一种变体
template<class ... Iterator>
void increment_dummy(Iterator ... i)
{}
template<class Function,class ... Iterator>
void for_each_combined(size_t N,Function&& fun,Iterator... iter)
{
while(N!=0)
{
fun(*iter...);
increment_dummy(++iter...);
--N;
}
}
Example usage
示例用法
void arrays_mix(size_t N,const float* x,const float* y,float* z)
{
for_each_combined(N,[](float x,float y,float& z){z=x+y;},x,y,z);
}