是否有一种安全(定义的行为)方法来使用 STL 来减少基于向量索引有效过滤向量的样板?

Ell*_*ith 5 c++ stl erase-remove-idiom stl-algorithm c++20

我经常发现自己想要根据向量的索引而不是值来过滤向量。

auto some_values = std::vector{1, 0, 4, 6, 2};

// Somewhere I figure out which items to remove.
// This may be from user interaction or something
// nothing to do with the original values:
const auto removing = std::vector<bool>{0, 0, 1, 0, 1};
Run Code Online (Sandbox Code Playgroud)

erase_if所以,我很想像这样使用:

std::erase_if(some_values, [it = removing.begin()](auto) mutable {return *it++;});
Run Code Online (Sandbox Code Playgroud)

它似乎适用于 gcc 和 clang。但是, std::erase cppref 页面上似乎没有任何关于谓词调用顺序的内容,所以我认为这是未定义的行为?

与 相同的问题std::remove_if。请注意,压缩范围不适用于大多数压缩选项,因为通常生成的范围无法调整基础数据的大小。

使用 for 循环并创建数组的副本并不是太多的样板文件,但我目前正在将过滤器应用于一些低级代码,在这些代码中我无法复制所有数据。有些数据会很大,需要在这些过滤操作期间做出响应。

最坏的情况我可以添加这样的函数来解决问题:

template <class T, auto N, class Pred>
size_t RemoveIfIndex(std::span<T, N> span, Pred&& pred)
{
  size_t write = 0; // Also behaves as a counter
  
  // Initialise to the first deleted index:
  for (; (write < span.size()) && !pred(write); ++write);
  
  // Shuffle the elements:
  for (size_t read = write + 1; read < span.size(); ++read)
  {
    if (!pred(read))
    {
      span[write] = span[read];
      ++write;
    }
  }
  return write;
}

template <class T, class Alloc, class Pred>
size_t EraseIfIndex(std::vector<T, Alloc>& vec, Pred&& pred)
{
  const size_t new_size = RemoveIfIndex(std::span{vec.begin(), vec.end()}, pred);
  vec.resize(new_size);
  return new_size;
}
Run Code Online (Sandbox Code Playgroud)

但是,当我需要添加类似的函数类别(需要索引信息)时,我犹豫是否要向我们的低级库添加更多代码,并且我怀疑我对范围/适配器/过滤器有一些了解缺少这一点将使这些功能变得不必要。

P K*_*mer -1

== 警告不正确的示例,出于教育目的而保留 == (remove_if 在内存中移动元素,因此索引在迭代期间不会对齐)

为什么是最坏的情况?拥有工作功能是一种很好的工作方式。它并不总是关于最少的代码行数。无论如何,我会创建一个函数来检查边界条件(例如向量大小应该匹配)

#include <algorithm>
#include <iostream>
#include <vector>
#include <stdexcept>

void remove_by_index(std::vector<int>& values, const std::vector<bool>& filter)
{
    if (values.size() != filter.size()) throw std::invalid_argument("");
    std::size_t index{ 0 };
    auto it = std::remove_if(values.begin(), values.end(), [&](const auto) { return filter[index++]; });
    values.erase(it, values.end());
}

int main()
{
    auto some_values = std::vector{ 1, 0, 4, 6, 2 };
    const auto remove = std::vector<bool>{ 0, 0, 1, 0, 1 };

    remove_by_index(some_values, remove);

    for (const auto value : some_values)
    {
        std::cout << value << " ";
    }

    return 0;
}
Run Code Online (Sandbox Code Playgroud)

  • 这就是UB,lambda每次调用都会得到不同的结果。 (3认同)