如何同时遍历两个或多个容器的最佳方法?

159

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

2
#include <algorithm> 中的 transform 怎么样? - Ankit Acharya
关于赋值循环:如果两者都是向量或类似的容器,使用containerA = containerB;代替循环。 - emlai
一个类似的问题:https://dev59.com/WGoy5IYBdhLWcg3wfeMR - knedlsepp
1
可能是Sequence-zip function for c++11?的重复问题。 - underscore_d
如果有人真的想要在单个循环中依次迭代两个容器,请查看https://dev59.com/vLHma4cB1Zd3GeqPJ1WM - Raven
非常适用于科学计算。为了代码的一致性,基于范围和迭代器的for循环似乎必须等待这样的应用程序。 - P. Nair
12个回答

1

C++17基本的“zip”实现

这个变体将不同的序列压缩成一个可以在结构化绑定中使用的元组向量。
您可以使用任意数量的序列。

非常简单和高效。

#include <iostream>
#include <utility>
#include <algorithm>
#include <cstddef>
#include <tuple>
#include <vector>

#include <string>
#include <array>
#include <map>

template<typename... Container>
auto zip(Container&... containers) noexcept {
    using tuple_type = std::tuple<std::decay_t<decltype(*std::begin(containers))>&...>;
    std::size_t container_size = std::min({ std::size(containers)... });

    std::vector<tuple_type> result;
    result.reserve(container_size);

    auto iterators = std::make_tuple(std::begin(containers)...);
    for (std::size_t i = 0; i < container_size; ++i) {
        std::apply([&result](auto&... it) { 
            result.emplace_back(*it++...);
        }, iterators);
    }

    return result;
}

int main() {
    std::vector v1 = { 1.1, 1.2, 1.3 };
    std::array v2 = { 1, 2, 3 };

    std::map<std::string, int> v3;
    v3["key1"] = 1;
    v3["key2"] = 2;
    v3["key3"] = 3;

    int arr[] = { 3, 2, 1 };


    for (const auto& [val1, val2, val3, val4] : zip(v1, v2, v3, arr)) {
        std::cout << "vector -> " << val1;
        std::cout << "\narray -> " << val2;
        std::cout << "\nmap -> " << val3.first << '|' << val3.second;
        std::cout << "\nC-array -> " << val4;
        std::cout << "\n\n";
    }

    std::cout.flush();

    return 0;
}

持续的方法

如果你想要更高效的方法,你也可以尝试这种constexpr的方法:
(请注意,结构化绑定必须始终为auto

#include <iostream>
#include <utility>
#include <algorithm>
#include <iterator>
#include <cstddef>
#include <tuple>

#include <vector>
#include <string>
#include <array>
#include <map>

template<typename... Container>
class zippable {
public:
    using tuple_type_t = std::tuple<std::decay_t<decltype(*std::begin(std::declval<Container&>()))>&...>;
    using iterator_tuple_t = std::tuple<decltype(std::begin(std::declval<Container&>()))...>;

    constexpr zippable(Container&... containers) noexcept : 
         iterators_begin(std::make_tuple(std::begin(containers)...)),
         iterators_end(std::make_tuple(std::end(containers)...)) {}

    class iterator {
    public:
        constexpr iterator(const iterator_tuple_t& it) noexcept : iterators(it) {}
        constexpr iterator(iterator_tuple_t&& it) noexcept : iterators(std::move(it)) {}

        constexpr iterator& operator++() {
            std::apply([](auto&... it) {
                ((it++), ...);
            }, iterators);

            return *this;
        }

        constexpr tuple_type_t operator*() {
            return std::apply([](auto&... it) -> tuple_type_t {
                return std::forward_as_tuple(*it...);
            }, iterators);
        }

        constexpr bool operator==(const iterator& other) const {
            return iterators == other.iterators;
        }

        constexpr bool operator!=(const iterator& other) const {
            return iterators != other.iterators;
        }
    private:
        iterator_tuple_t iterators;
    };

    constexpr iterator begin() noexcept {
        return iterator(iterators_begin);
    }

    constexpr iterator end() noexcept {
        return iterator(iterators_end);
    }
private:
    iterator_tuple_t iterators_begin;
    iterator_tuple_t iterators_end;
};

int main() {
    std::vector v1 = { 1.1, 1.2, 1.3 };
    std::array v2 = { 1, 2, 3 };

    std::map<std::string, int> v3;
    v3["key1"] = 1;
    v3["key2"] = 2;
    v3["key3"] = 3;

    int arr[] = { 3, 2, 1 };

    for (auto [val1, val2, val3, val4] : zippable(v1, v2, v3, arr)) {
        std::cout << "vector -> " << val1;
        std::cout << "\narray -> " << val2;
        std::cout << "\nmap -> " << val3.first << '|' << val3.second;
        std::cout << "\nC-array -> " << val4;
        std::cout << "\n\n";
    }

    std::cout.flush();

    return 0;
}

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

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接