从结构的向量中,获得一个为每个结构收集一个字段的向量

时间:2019-02-26 06:58:56

标签: c++

假设我具有以下结构:

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个字段,因此为每个字段编写一个方法有点艰巨和乏味。

6 个答案:

答案 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::transformstd::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::transformstd::back_inserterstd::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值(这可能是非常大的内存),您将可以快速访问它们。另一方面,缓存一个点的局部性将变得更糟。因此,如果您有性能问题的衡量标准,那么可以在哪里找到它们。