我想使用 STL 算法简化索引工作(当您有另一个容器的索引数组以加快访问速度时,比如说对索引进行排序)。我想通过索引对数据进行操作,这对程序员来说是透明的。例如,我想使用 std::equal_range 使用索引查找数据中的范围,而不为此编写谓词(该谓词只有两个目的:存储数据容器的 begin() 并为间接提供代码访问)。
我问这个问题是因为我觉得我正在重新发明自行车,STL 应该有一些东西来解决这个任务,比如它有 less、greater、negate 等谓词。我希望有人回答“只需使用谓词” std::indirect_array 和 std::indirect_binary_predicate 以这种方式......”或类似的东西。我更喜欢适用于通常的 std::vector 容器的解决方案。
我想避免什么
如果我需要对索引数据使用 STL 算法(假设我有带有值的数组 data 和带有索引的数组 indexes),该算法使用数据容器 begin() 引用索引数据(假设现在我们使用 std::vector )和索引(取自索引数组)。
我更喜欢保留索引(而不是指针/迭代器),主要是因为我想减少内存占用。对于 Winx64 平台,每个 int 索引占用 4 个字节,而指针占用 8 个字节。此外,索引的使用简化了数组重定位,适合处理数组结构 (SoA) 和一些其他操作。
在我看来,问题分为两个:
我真的无法将这两者解耦,如果有人可以,请提供方法。主要原因是,当我问在哪里以及如何存储容器 begin() 时,人们会指出具有内部状态的谓词,这正是问题的第二部分。
当我谈论透明度时,我的意思是我想使用像 std::equal_range 这样的算法来查找值的范围,而不是在索引中,而是使用索引作为入口点。不幸的是, std::equal_range 对我的意图一无所知,只会比较索引。当然,我可以在比较器谓词中处理这个问题,但这将迫使我编写它,并且在每个谓词中记住间接寻址。
节省内存,我必须回报,将容器的 begin() 保留在某处(或提供)。
因此,最有可能的是,我必须将 begin() 传递给算法,而最好的位置是谓词:
struct Value {
int data;
public:
Value(int value) : data(value) {}
operator int() const { return data; }
bool operator < (const Value& rhs) const { return data < int(rhs); }
};
struct Index {
public:
int index;
public:
Index(int i) : index(i) {}
};
class Comparator {
const std::vector<Value>& vec;
public:
Comparator(const std::vector<Value>& vec) : vec(vec) {}
bool operator () (const Value& lhs, const Index& rhs) const {
return lhs < vec[rhs.index];
}
bool operator () (const Index& lhs, const Value& rhs) const {
return vec[lhs.index] < rhs;
}
};
int main()
{
std::vector<Value> v1 = { 0,30,20,40,10 };
std::vector<Index> i1 = { 0,4,2,1,3 };
Value value(30);
auto range_pred = std::equal_range(i1.begin(), i1.end(), value, Comparator(v1));
std::cout << std::endl << "With predicate:" << std::endl;
std::cout << "*range_pred.first = " << v1[(*range_pred.first).index] << std::endl;
std::cout << "*range_pred.second= " << v1[(*range_pred.second).index] << std::endl;
std::cout << std::endl << "range_pred.second-range_pred.first " << std::distance(range_pred.first, range_pred.second) << std::endl;
}
关键问题是:
关键问题
有目标
我确信在现代 C++/STL 中应该有一些现成的解决方案。
我有几十个索引、几十个谓词和许多间接级别,并且希望简化代码,因为在许多地方它变得复杂,同时虽然不完全相同,但很相似。大部分都是间接代码。
不完全确定这是否是您所期望的,但通常索引是通过投影实现的,正如 @Jarod42 在评论中指出的那样。您还可以使用自定义视图,如此处所示。
#include <vector>
#include <concepts>
#include <ranges>
#include <algorithm>
#include <iostream>
struct Value {
int data;
public:
Value(int value) : data(value) {}
operator int() const { return data; }
bool operator < (const Value& rhs) const { return data < int(rhs); }
};
struct Index {
public:
int index;
public:
Index(int i) : index(i) {}
};
template<std::ranges::random_access_range R>
auto indexing(R&& range){
return std::views::transform([&range](const Index& i){
return *(std::ranges::begin(range)+i.index);
});
};
int main()
{
std::vector<Value> v1 = { 0,30,20,40,10 };
std::vector<Index> i1 = { 0,4,2,1,3 };
Value value(30);
//Using projection
auto range_pred1 = std::ranges::equal_range(i1, value, {}, [&v1](auto&& i){return (*(std::ranges::begin(v1)+i.index));});
std::cout << "*range_pred.first = " << v1[(*range_pred1.begin()).index].data << std::endl;
std::cout << "*range_pred.second= " << v1[(*range_pred1.end()).index].data << std::endl;
std::cout << std::endl << "range_pred.second-range_pred.first " << std::distance(range_pred1.begin(), range_pred1.end()) << std::endl;
//Using custom view
auto indexingView = i1 | indexing(v1);
auto range_pred = std::ranges::equal_range(indexingView, value);
std::cout << "*range_pred.first = " << (*range_pred.begin()).data << std::endl;
std::cout << "*range_pred.second= " << (*range_pred.end()).data << std::endl;
std::cout << std::endl << "range_pred.second-range_pred.first " << std::distance(range_pred.begin(), range_pred.end()) << std::endl;
}