我可以迭代类成员,就好像它们是C ++中的数组一样吗?

时间:2017-06-27 15:35:27

标签: c++ arrays

假设我有一个类似于以下的类:

struct Potato {
    Eigen::Vector3d position;
    double weight, size;
    string name;
};

以及Potato s

的集合
std::vector<Potato> potato_farm = {potato1, potato2, ...};

这显然是一种结构阵列(AoS)布局,因为,就大多数情况而言,将所有Potato的数据集中在一起是有意义的。但是,我可能想要计算一个最常见的名称,其中数组结构(SoA)设计使事物与具有名称的事物类型无关(一组人都有名字,一组地方,所有的名字等等。)C ++是否有任何工具或技巧使AoS布局看起来像SoA一样做这样的事情,还是有更好的设计来完成同样的事情?

4 个答案:

答案 0 :(得分:4)

您可以使用lambdas访问在范围内工作的algos中的特定成员:

double mean = std::accumulate( potato_farm.begin(), potato_farm.end(), 0.0, []( double val, const Potato &p ) { return val + p.weight; } ) / potato_farm.size();

如果这还不够,你不能让它看起来像数据数组,因为它需要对象在连续的内存中,但你可以使它像一个容器。因此,您可以实现自定义迭代器(例如type == double的随机访问迭代器,它迭代权重成员)。如何实现自定义迭代器的描述是here。你甚至可以制作那种通用的,但目前尚不清楚这是否会使工作更糟糕,因为它不是很容易实现。

答案 1 :(得分:1)

不幸的是,没有语言工具可以将结构一般性地更改为SoA。当你试图将SIMD编程提升到更高水平时,这实际上是一个很大的障碍。

您需要手动创建SoA。但是,您可以通过创建对SoA对象的引用来帮助自己,就像它是一个普通的Potato一样。

struct Potato {
    float position;
    double weight, size;
    std::string name;
};
struct PotatoSoARef {
    float& position;
    double& weight;
    double& size;
    std::string& name;
};
class PotatoSoA {
private:
    float* position;
    double* weight;
    double* size;
    std::string* name;
public:
    PotatoSoA(std::size_t size) { /* allocate the SoA */ }
    PotatoSoARef operator[](std::size_t idx) {
        return PotatoSoARef{position[idx], weight[idx], size[idx], name[idx]};
    }
};

这样,无论您是否拥有Potatos的AoS或SoA,您都可以将其字段作为arr[idx].position等访问(均为r值和l值)。编译器可能会优化代理。

您可能还想添加其他构造函数和访问器。

如果您希望函数具有针对AoS和SoA访问模式的统一接口,您可能还有兴趣实现常规AoS,operator[]返回PotatoSoARef

如果您愿意偏离C ++,您可能会对语言扩展感兴趣,例如Sierra

答案 2 :(得分:0)

正如Slava所说,如果不编写自己的迭代器,你就不会从AoS数据中获得类似SoA的访问权限,而且我认为真的很难确定STL算法的使用是否是那样的在这之前很重要,特别是如果这不是一般的解决方案。无论如何,SoA数据的主要好处是缓存性能,而不是您正在使用的任何容器的特定语法,除了实际的SoA数据之外什么都不会让您获得。

答案 3 :(得分:0)

使用range-v3(不在C ++ 17中: - /),您可以使用投影或转换视图:

ranges::accumulate(potato_farm, 0., ranges::v3::plus{}, &Potato::weight);

auto weightsView = potato_farm | ranges::view::transform([](auto& p) { return p.weight; });
ranges::accumulate(weightsView, 0.);