【问题标题】:Range based for loop: Iterate over vector extended with one element基于范围的for循环:迭代一个元素扩展的向量
【发布时间】:2020-06-01 23:14:25
【问题描述】:

我希望在 C++ 中实现与此 Python 代码等效的更少(但内存效率更高):

a = [1, 5, 3]
additional = 6
for elem in [additional] + a:
    print(elem) # prints: 6 1 5 3

# alternative without creating the additional vector:
import itertools
for elem in itertools.chain([additional], a):
    print(elem)

我知道在 C++ 中执行此操作的唯一方法是:

#include <iostream>
#include <vector>

int main() {
        std::vector<int> a = {1, 5, 3};
        int additional = 6;

        for (int i = 0; i < a.size() + 1; ++i) {
                int cur_elem;
                if (i == 0) {
                        cur_elem = additional;
                } else {
                        cur_elem = a[i-1];
                }
                std::cout << cur_elem << std::endl;
        }
}

有没有办法使用基于范围的 for 循环来做到这一点?我找到了 Boost 连接运算符,但它似乎只使用可迭代对象,因此我需要创建一个额外的向量(如 Python 示例中所示)。

理想情况下,迭代不会在内存中创建连接对象并使用标准库的算法。

【问题讨论】:

  • 为什么不在循环之前打印出来?
  • 当你想在基于循环的范围内做一些不寻常的事情时,你总是可以编写一个自定义的迭代器类型来做任何你想做的事情。
  • 这能回答你的问题吗? Why no push/pop in front of vector? 基本上,push_front() 附加到集合的开头,但您应该使用 std::list 而不是 vector
  • @KenY-N std::list 的正当理由很少见。基本上,除非迭代器/引用失效规则对您的应用程序非常重要,否则可能有比std::list 更好的容器。这里是std::deque
  • @KenY-N 另外,他提到他想避免仅仅为了迭代而创建一个新容器,我同意他的观点。事实上,新范围库的卖点之一就是能够轻松做到这一点。 (我的观点是我不认为这是上述问题的重复)

标签: c++ for-loop iterator range


【解决方案1】:

可以使用即将推出的范围功能来完成。

这是一个使用 Eric Niebler 的 range-v3 库的示例:

#include <iostream>
#include <vector>

#include <range/v3/view/concat.hpp>
#include <range/v3/view/single.hpp>

int main() {
    std::vector<int> a = {1, 5, 3};
    int additional = 6;
    for (auto i : ranges::concat_view(ranges::single_view{additional}, a)) {
        std::cout << i;
    }
}

See it live!

通过使用视图,所有迭代器操作都是惰性的,并且不使用额外的内存(例如:不创建额外的向量/数组)

或者,没有for 循环:

ranges::copy(ranges::concat_view(ranges::single_view{additional}, a), ranges::make_ostream_joiner(std::cout, ","));

See it live!

(老实说,我更喜欢for 版本)

符合标准的解决方案

上面的解决方案有个小问题:concat_view 没有进入 C++20。如果您想要一个严格合规的解决方案,您可能需要创建自己的版本,或者改用join_view

#include <iostream>
#include <vector>

#include <ranges>

int main() {
    std::vector<int> a = {1, 5, 3};
    int additional = 6;

    std::vector v{{additional}, a};

    for(int i : std::ranges::join_view{v}) {
        std::cout << i;
    }
}

【讨论】:

  • 显然,我的循环体不仅仅是一个打印,因此显式循环更好。你知道为什么 concat_view 没有成功吗?将来的版本中是否会出现?
  • @gerion 这根本不是最初提案的一部分。 ranges-v3 已经很庞大了,因此一次将整个库添加到标准中是不可行的。 Eric 确实在他的blog 中提到(请看最后的“未来方向”部分)他可能会提议在未来将它与其他几个适配器一起添加,但我不知道何时(或是否)会是。
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2015-04-26
  • 2020-10-05
  • 2020-05-03
  • 1970-01-01
  • 2015-08-27
  • 1970-01-01
相关资源
最近更新 更多