如何顺序地遍历两个容器

时间:2020-10-15 12:55:37

标签: c++

我有两个向量。我想遍历这两个元素的所有元素并做一些事情(比如将它们打印出来)。所以我可以这样写:

vector<int> vec_a{1, 2, 3}, vec_b{4, 5, 6, 7};

for (auto a : vec_a) {
  cout << a;
}
for (auto b : vec_b) {
  cout << b;
}

这有很多重复。我可以做类似的事情:

for (const auto& vec : {vec_a, vec_b}) {
  for (auto elem : vec) {
    cout << elem;
  }
}

但这增加了一个额外的for(虽然还不错,但我想知道是否还有更好的方法。

for (auto elem : concat(vec_a, vec_b)) {
  cout << elem;
}

我知道我可以只合并向量(la Concatenating two std::vectors),但是语法甚至更笨拙(特别是因为我实际上有4个向量)。

我希望输出为:

1 2 3 4 5 6 7

3 个答案:

答案 0 :(得分:11)

一个简单的解决方案是使用辅助函数:

#include <functional>

template <typename Func, typename... Containers>
void for_all(Func&& func, Containers&&... containers) {
    auto iteration_func = [&](auto&& container) {
        for (auto&& elem : std::forward<decltype(container)>(container)) {
            std::invoke(func, std::forward<decltype(elem)>(elem));
        }
    };

    (iteration_func(std::forward<Containers>(containers)), ...);
}

在这里,我们使用fold expression和立即调用的lambda来模拟可变参数模板参数上的循环,其中每个参数都被循环,并在其元素上调用提供的函数对象。

使用forwarding references和对std::forward的调用会保留参数和元素的值类别,以与rvalue范围兼容(例如range-v3库中的move_view)。 std::invoke将功能对象的概念概括为pointers to members,这在某些情况下很有用。

示例:

int main() {
    std::vector<int> vec_a{1, 2, 3};
    std::vector<int> vec_b{4, 5, 6, 7};
    
    for_all([](int n) {
        std::cout << n << ' ';
    }, vec_a, vec_b);
    
    std::cout << '\n';
}

wandbox

可以混合使用不同的容器类型:

for_all([](const auto& n) {
    std::cout << n << ' ';
}, std::vector{1, 2, 3}, std::list{"foo", "bar"});

答案 1 :(得分:1)

有一个不错的range library可以与C++14一起使用,并且在很大程度上将成为C ++ 20的一部分。 该库中有ranges::views::concat,它以干净的方式完全满足您的需求:

#include <iostream>
#include <ranges>
#include <vector>
#include <array>
#include <functional>
#include <range/v3/view/concat.hpp>

int main()
{
    using ranges::views::concat;
    
    auto a = std::vector<int>{ 1, 2, 6, 7 };
    auto b = std::array<int , 2>{ 1, 3 };
    auto c = { -1, -4, 7, 8, 9, 11};

    for (auto x : concat(a, b)) {
        std::cout << x << ", ";
    }
    std::cout << "\n--\n";

    for (auto x : concat(c, a, b)) {
        std::cout << x << ", ";
    }

    return 0;
}

https://godbolt.org/z/Waf3Ma

可悲的是concat在C ++ 20中不可用(在文档中找不到它,而gcc不支持它。)

答案 2 :(得分:0)

如果要减少 code ,只需编写一个函数即可迭代:

void iterate(const std::vector<int>& t)
{
    for(auto i : t)
    {
        std::cout << i << ' ';
    }
}

vector<int> a{1, 2, 3}, b{4, 5, 6};

iterate(a);
iterate(b);

编写该函数后,您可以改写上面L.F.的想法,并编写一个函数,该函数将对传递的任意数量的数组进行迭代:

template <class ... T>
void iterate_many(T ... args)
{
    (void) std::initializer_list<int>{
        ((void) iterate(args), 0)...
    };
}

这是完整的示例:

#include <iostream>
#include <vector>
using namespace std;
 
void iterate(const std::vector<int>& t)
{
    for(auto i : t)
    {
        std::cout << i << ' ';
    }
}
 
template <class ... T>
void iterate_many(T ... args)
{
    (void) std::initializer_list<int>{
        ((void) iterate(args), 0)...
    };
}
 
int main() {
    vector<int> a{1, 2, 3}, b{4, 5, 6}, c{7, 8, 9};
 
    iterate(a);
    iterate(b);
 
    std::cout << '\n';
 
    iterate_many(a, b, c);
 
    return 0;
}

ideone