以类似方式访问数组中的结构成员:在不同于数组的结构中填充?

accessing struct members in array like manner: padding in structs different than in arrays?

假设我有一个包含多个成员的结构,每个成员都属于同一类型。我想通过名称和数组索引安全地访问成员,所以我引入了一个 union ,它包含实际的 struct 和一个数组,它对应于 [=13 的成员=] 在数量和类型方面。请参阅以下代码,它说明了我如何通过使用这样的 union:

以两种不同的方式 "view" struct
#include <iostream>

template<typename elemT>
struct productData {
    elemT color;
    elemT size;
    elemT application;
    elemT division;
};

template<typename elemT>
union productDataAsArray {
    struct productData<elemT> p;
    elemT arr[(sizeof(productData<elemT>) / sizeof(elemT))];
};

int main() {

    union productDataAsArray<int> myProduct;
    myProduct.p.color = 10;
    myProduct.p.size = 20;
    myProduct.p.application = 30;
    myProduct.p.division = 40;

    for (int i=0; i<4; i++) {
        std::cout << "elem #" << i << ":" << myProduct.arr[i] << std::endl;
    }
}

标准假设数组的内存布局对应于struct的内存布局是否安全/保证,这样两种不同的访问方法对于任何类型都可以达到预期的结果elemT?

以下static_asserts几种不同的类型表明它应该可以工作。但真的有保障吗?

static_assert(sizeof(productData<int>)==sizeof(productDataAsArray<int>::arr), "padding/alignment inconsistency");
static_assert(sizeof(productData<char>)==sizeof(productDataAsArray<char>::arr), "padding/alignment inconsistency");
static_assert(sizeof(productData<double>)==sizeof(productDataAsArray<double>::arr), "padding/alignment inconsistency");
static_assert(sizeof(productData<char*>)==sizeof(productDataAsArray<char*>::arr), "padding/alignment inconsistency");

class alignas(16) testClass {
    int x;
    int y;
    virtual void test() {};
};

static_assert(sizeof(productData<testClass>)==sizeof(productDataAsArray<testClass>::arr), "padding/alignment inconsistency");

这里先不说内存布局了。

您已经通过读取 union 成员而不是最后分配给的成员来调用 UB。在标准C++中也不例外。

明确地说:您所有 "Is it safe/guaranteed" 问题的答案是 "No"。

But is it actually guaranteed?

没有

您通过访问 union.

的非活动成员调用 未定义行为

您的方法是 UB,您可以改为执行以下操作:

template<typename elemT>
class productData {
    elemT data[4];
public:
    const elemT& operator[](int i) const { return data[i];}
    const elemT& color() const { return data[0]; }
    const elemT& size() const { return data[1]; }
    const elemT& application() const { return data[2]; }
    const elemT& division() const { return data[3]; }

    elemT& operator[](int i) { return data[i];}
    elemT& color() { return data[0]; }
    elemT& size() { return data[1]; }
    elemT& application() { return data[2]; }
    elemT& division() { return data[3]; }
};

正如其他帖子所提到的,不能保证。

您的 static_assert 也不足以检查,因为您可以在不同的地方填充。您应该在静态断言中明确验证内存位置:

namespace {
const productDataAsArray<int> testObj{0};
static_assert(&testObj.p.color == &testObj.arr[0]);
static_assert(&testObj.p.size == &testObj.arr[1]);
static_assert(&testObj.p.application == &testObj.arr[2]);
static_assert(&testObj.p.division == &testObj.arr[3]);
}

更好的解决方案是使用枚举索引:

enum productDataIndexes {
  PRODUCT_COLOR = 0,
  PRODUCT_SIZE,
  PRODUCT_APPLICATION,
  PRODUCT_DIVISION,
  NUM_PRODUCT_INDEXES
};

int main() {
    ...
    myProduct[PRODUCT_COLOR] = 10;
    myProduct[PRODUCT_SIZE] = 20;
    myProduct[PRODUCT_APPLICATION] = 30;
    myProduct[PRODUCT_DIVISION] = 40;

    for (int i=0; i<NUM_PRODUCT_INDEXES; i++) {
        std::cout << "elem #" << i << ":" << myProduct[i] << std::endl;
    }
}