132

C++11 提供了多种迭代容器的方法。例如:

基于范围的循环

for(auto c : container) fun(c)

std::for_each

for_each(container.begin(),container.end(),fun)

但是,推荐的方法是迭代两个(或更多)相同大小的容器以完成以下操作:

for(unsigned i = 0; i < containerA.size(); ++i) {
  containerA[i] = containerB[i];
}
4

10 回答 10

68

派对迟到了。但是:我会遍历索引。但不是使用经典for循环,而是使用基于范围for的索引循环:

for(unsigned i : indices(containerA)) {
    containerA[i] = containerB[i];
}

indices是一个简单的包装函数,它返回索引的(惰性求值)范围。由于实现(虽然简单)有点太长,无法在此处发布,您可以在 GitHub 上找到实现

此代码与使用手动经典循环一样有效。for

如果这种模式经常出现在您的数据中,请考虑使用另一种模式,它包含zip两个序列并生成一系列元组,对应于成对的元素:

for (auto& [a, b] : zip(containerA, containerB)) {
    a = b;
}

的实现zip留给读者作为练习,但它很容易从indices.

(在 C++17 之前,您必须改为编写以下代码:)

for (auto items&& : zip(containerA, containerB))
    get<0>(items) = get<1>(items);
于 2013-09-25T13:19:57.697 回答
40

对于您的具体示例,只需使用

std::copy_n(contB.begin(), contA.size(), contA.begin())

对于更一般的情况,您可以使用 Boost.Iterator 的zip_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";

活生生的例子。

但是,对于全面的通用性,您可能需要更像这样的东西,这将适用于在其命名空间中没有成员begin()/end()begin/end函数的数组和用户定义类型。const此外,这将允许用户通过这些zip_c...功能专门获得访问权限。

如果你像我一样提倡漂亮的错误消息,那么你可能想要this,它检查是否有任何临时容器被传递给任何zip_...函数,如果是,则打印一个漂亮的错误消息。

于 2012-09-23T15:04:59.103 回答
37

我想知道为什么没有人提到这一点:

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 块中。

于 2016-03-28T10:36:01.503 回答
9

有很多方法可以使用标题中提供的多个容器来执行特定操作。algorithm例如,在您给出的示例中,您可以使用std::copy而不是显式 for 循环。

另一方面,除了普通的 for 循环之外,没有任何内置方法可以通用地迭代多个容器。这并不奇怪,因为有很多迭代方法。想一想:你可以用一个步骤迭代一个容器,用另一个步骤迭代一个容器;或通过一个容器直到它到达末端,然后在您穿过另一个容器的末端时开始插入;或者每次您完全通过另一个容器时第一个容器的一个步骤然后重新开始;或其他模式;或一次超过两个容器;ETC ...

但是,如果您想创建自己的“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;
  Container2::iterator i2;
  for (i1 = begin1, i2 = begin2; (i1 != end1) && (i2 != end2); ++it1, ++i2) {
    f(i1, i2);
  }
}

显然,您可以以类似的方式制定您想要的任何类型的迭代策略。

当然,您可能会争辩说,直接执行内部 for 循环比编写这样的自定义函数更容易……而且您是对的,如果您只打算执行一两次。但好的是,这是非常可重用的。=)

于 2012-09-23T14:40:41.710 回答
8

如果您只需要同时迭代 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;
}

当您需要在一个算法中处理超过 2 个容器时,您需要使用 zip。

于 2014-04-29T19:01:23.413 回答
6

另一种解决方案可能是在 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++; })

在 lambda 中,你可以做任何事情ita,然后增加它。这很容易扩展到多个容器的情况。

于 2016-06-09T20:15:04.963 回答
5

范围库提供了这个和其他非常有用的功能。以下示例使用Boost.RangeEric 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 将通过结构化绑定使这一点变得更好:

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;
}
于 2016-07-25T14:12:53.197 回答
3

我也有点晚了;但你可以使用这个(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);

或者这个(使用函数参数包):

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);

或者这个(使用大括号括起来的初始化列表):

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});

或者你可以像这里一样加入向量:连接两个向量的最佳方法是什么?然后迭代大向量。

于 2017-07-28T11:43:29.813 回答
2

如果可能的话,我个人更喜欢使用 STL 中已经存在的内容(在<algorithm>标题中)。std::transform有一个可以接受两个输入迭代器的签名。因此,至少对于两个输入容器的情况,您可以这样做:

std::transform(containerA.begin(), containerA.end(), containerB.begin(), outputContainer.begin(), [&](const auto& first, const auto& second){
    return do_operation(first, second);
});

请注意,outputContainer也可以是输入容器之一。但一个限制是,如果您正在修改其中一个容器,则无法执行更新后操作。

于 2020-09-23T21:28:56.553 回答
0

这是一种变体

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;
        }
    }

示例用法

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);    
    }
于 2017-04-18T07:54:28.443 回答