假设我具有以下结构:
struct Point {
double X,Y,Z;
};
和以下向量:
std::vector<Point> v;
// populate v with random points
现在,我想调用类似collect(v, X)
之类的东西,并从原始结构向量中获取一个包含std::vector
值的X
,例如:
v.push_back(Point{1.0, 2.0, 3.0});
v.push_back(Point{1.1, 0.0, -0.5});
auto ans = collect(v,X);
// ans = [1.0, 1.1]
我认为这是一个很常见的任务,并且我敢肯定有一个好名字,我在问的时候就想不出来(请随时指出我!)。
我可以这样做:
std::vector<double> collectX(std::vector<Point> v) {
std::vector<double> output;
for (auto elem : v) {
output.push_back(elem.X);
}
}
/* Repeat for each field the struct Point has... */
我知道C ++没有体现。我想知道是否有解决方法?就像您想象的那样,我正在使用的结构没有3个字段,因此为每个字段编写一个方法有点艰巨和乏味。
答案 0 :(得分:5)
因此为每个字段编写一个方法有点艰巨和乏味
对此的直接解决方案是也将字段标识符作为参数传递。
std::vector<double> collect(double Point::* f, std::vector<Point> const& v) {
std::vector<double> output;
for (auto const& elem : v) {
output.push_back(elem.*f);
}
return output;
}
要这样称呼:
collect(&Point::X, v);
如果类型并非总是double
,则可以轻松地将上面的类型做成成员类型的模板:
template<typename T>
std::vector<T> collect(T Point::* f, std::vector<Point> const& v) {
std::vector<T> output;
for (auto const& elem : v) {
output.push_back(elem.*f);
}
return output;
}
最后,您正在寻找这种提取的术语是“投影”。即,将函数投影到轴上时得到的结果非常粗略。在我们的例子中,该函数将向量的索引映射到Point
,并且投影照原样在x
轴上。
它也可以使用C ++标准库或ranges-v3库即时编写。投影是对项目范围的非常普遍的操作,因此许多以范围为中心的库都将具有执行此操作的功能。
答案 1 :(得分:1)
您可以为此使用std::transform
和std::back_inserter
。
std::vector<Point> v;
v.push_back(Point{1.0, 2.0, 3.0});
v.push_back(Point{1.1, 0.0, -0.5});
std::vector<double> x;
std::transform(v.begin(), v.end(), std::back_inserter(x),
[](Point const& p) -> double { return p.x; });
答案 2 :(得分:1)
使用std::transform
,std::back_inserter
和std::mem_fn
:
#include <functional>
//...
std::vector<Point> v{{0,1,2},{9,8,7}};
std::vector<double> x;
std::transform(v.begin(), v.end(), std::back_inserter(x),
std::mem_fn(&Point::x));
编译器通常可以优化std::mem_fn
后面的间接访问。
答案 3 :(得分:0)
range-v3 +宏解决方案,可用于所有字段和类型:
#define view_extract_field(fname) (view::transform([](const auto& val) { return val.fname; }))
auto vx = v | view_extract_field(X) | to_vector;
答案 4 :(得分:0)
您可以将模板用于此类内容
template<typename C, typename F>
auto vmap(F f, const C& c) -> std::vector<decltype(f(*c.begin()))> {
std::vector<decltype(f(*c.begin()))> res;
for (auto& x : c) res.push_back(f(x));
return res;
}
用作
auto x_values = vmap([](const Point& p){ return p.x; }, pts);
vmap(f, c)
返回适用于std::vector
元素的所有f
的{{1}},并且c
是任何标准容器。
要提取c
,我将x
用作lambda。
答案 5 :(得分:0)
这不能直接回答您的问题。如果您担心性能并经常需要执行此拆分,那么考虑使用其他存储设计可能会很有用。
class Points {
public:
//Constructor ...
std::vector<double> & getX() const;
private:
std::vector<double> x;
std::vector<double> y;
std::vector<double> z;
};
std::vector<double> & Points::getX() {
return x;
}
通过这种方式,您不必复制点的x值(这可能是非常大的内存),您将可以快速访问它们。另一方面,缓存一个点的局部性将变得更糟。因此,如果您有性能问题的衡量标准,那么可以在哪里找到它们。