【问题标题】:Best way to remove elements from a container using its ranges version使用其范围版本从容器中删除元素的最佳方法
【发布时间】:2019-04-22 04:53:02
【问题描述】:

我在我的代码中遇到一个常见问题,我想在它满足谓词后从反转的 std::vector 中只删除一个元素。我知道使用 range-v3 有多种方法可以做到这一点,但我想出的每种方法似乎都有些复杂。

这是目标向量 v 的示例:

std::vector v = { 1, 2, 3, 2, 4 };

结果需要是向量r:

std::vector r = { 1, 2, 3, 4 };

这将通过删除反向遍历向量 v 时找到的前 2 个(通过 lambda 谓词“is_two”)来完成。

这是在普通 C++ 原始循环中的样子:

auto is_two = [](int a) { return a == 2; };

for (int i = v.size(); --i >= 0;) {

    if (is_two(v[i])) {

        v.erase(v.begin() + i);
        break;
    }
}

这是我的坏范围-v3 版本:

namespace rs = ranges;
namespace rv = ranges::view;
namespace ra = ranges::action;

rs::for_each(v | rv::enumerate
               | rv::reverse
               | rv::filter([](auto i_e) { return i_e.second == 2; })
               | rv::take(1),

            [&](auto& i_e) { v.erase(v.begin() + i_e.first); });

理想情况下,我想知道是否有一些看起来像这样的解决方案:

ra::remove_if(v | rv::reverse, is_two);

概括地说,我想知道如何获取一个容器,通过一些 range::view 操作对其进行管道传输,然后从原始容器中删除结果范围内的元素。

【问题讨论】:

  • 关键问题是如何将视图上的迭代器转换为原容器对应的迭代器。

标签: c++ range


【解决方案1】:

由于似乎没有人想出更好的方法, 为了您的利益,我想提一下诉诸老 reverse_iterators 的可能性。

vec.erase(std::prev(ranges::find_if(vec.rbegin(), vec.rend(), is_two).base()));

诚然,这不是很范围, 但至少它有效。

【讨论】:

  • 这实际上比我的用例中的解决方案要好,谢谢。你能解释一下 .base() 的作用吗?再次感谢。
  • @IanCaburian cppreference page on reverse_iterator 详细解释了这一点。 base() 返回由 1 w.r.t 关闭的底层迭代器。实际位置,因此是prev
【解决方案2】:

基于范围的 for 循环的主要目的是保持一致性。对每个元素执行相同的操作。

删除一个元素会破坏这种一致性。所以最好的解决方案是一个普通的 for 循环,你可以在其中使用迭代器和中断。

当你有一把锤子时,一切看起来都像钉子。不要将远程循环敲打成正常循环。

【讨论】:

    猜你喜欢
    • 2021-08-10
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2010-12-01
    相关资源
    最近更新 更多