9

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

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

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

我希望输出为:

1 2 3 4 5 6 7

4

3 回答 3

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

在这里,我们使用带有立即调用的 lambda的折叠表达式来模拟可变参数模板参数的循环,其中每个参数都被循环并在其元素上调用提供的函数对象。

使用转发引用和调用来std::forward保留参数和元素的值类别,以与右值范围兼容(例如,move_view来自 range-v3 库)。 std::invoke将函数对象的概念概括为指向成员的指针,这在某些情况下很有用。

例子:

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

魔杖盒

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

for_all([](const auto& n) {
    std::cout << n << ' ';
}, std::vector{1, 2, 3}, std::list{"foo", "bar"});
于 2020-10-15T13:11:22.863 回答
1

有一个很好的范围库可以与C++20 一起使用,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 不支持它)。

于 2020-10-16T09:42:03.763 回答
0

如果你想减少代码,你可以简单地写一个函数来迭代:

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

编写完该函数后,您可以采用上述 LF 的想法并编写一个函数,该函数将遍历您传递的任意数量的数组:

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

于 2020-10-15T13:23:04.593 回答