我经常发现自己想根据它的index而不是它的值来过滤一个向量。
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};
所以,我很想像这样使用
erase_if
:
std::erase_if(some_values, [it = removing.begin()](auto) mutable {return *it++;});
它似乎适用于 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;
}
但是当我需要添加类似类别的函数(需要索引信息)时,我对向我们的低级库添加更多代码犹豫不决,而且我怀疑我对范围/适配器/过滤器有一些了解'我缺少这会使这些功能变得不必要。
它可能符合也可能不符合您对“STL”的看法,但
std::valarray
更普遍地支持像这样的掩码阵列,因此您可以这样做:
#include <valarray>
#include <iostream>
int main() {
std::valarray<int> values { 1, 2, 3, 4, 5 };
std::valarray<bool> mask { 0, 1, 1, 0, 1 };
// zero all the values where there's a 1 in the mask array
values[mask] = 0;
// sum where there were zeros in the mask array:
std::cout << values.sum(); // result: 5 (1+4)
}
这也适用于数组的内容。例如,您可以指定一个条件(其中 valarray 的名称充当一种占位符):
#include <valarray>
#include <iostream>
int main() {
std::valarray<int> values { 1, 2, 3, 4, 5 };
values[values > 3] = 0;
// values = { 1, 2, 3, 0, 0 };
std::cout << values.sum(); // 6 (1+2+3)
}
所以通常不像你说的那样removing元素,但是根据情况,你可能会得到类似的效果,基本上忽略你不需要/不想要的元素。
免责声明:主要是将此发布为很少有人知道的有趣内容。老实说,很多人都质疑它到底有多有用——
valarray
可以做一些很酷的事情,但它也有一些相当奇怪的局限性。它很少被使用是有充分理由的。
你是对的,你的
erase_if
电话不能保证有效。
根据您需要过滤元素的用途,不删除它们可能是可行的,而是构建一个表以在原始向量中查找它们。沿着线的东西......
#include <vector>
#include <iostream>
template <typename T>
struct filtered_vector {
std::vector<T>& data;
std::vector<size_t> indices;
size_t s;
filtered_vector(std::vector<T>& data,const std::vector<bool>& removing) : data(data) {
size_t count = 0;
for (const auto& r : removing) {
if (!r) indices.push_back(count);
++count;
}
}
T& operator[](size_t index) {
return data[indices[index]];
}
size_t size() { return indices.size();}
};
int main() {
auto some_values = std::vector{1, 0, 4, 6, 2};
const auto removing = std::vector<bool>{0, 0, 1, 0, 1};
filtered_vector<int> f{some_values,removing};
for (size_t i=0;i<f.size();++i) std::cout << f[i] << " ";
}
作为旁注,如果您确实经常擦除和插入元素但同时需要对其他元素的稳定引用,则可能值得考虑
std::list
使用稳定的迭代器。
==警告不正确的示例,留作教育目的== (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;
}