【问题标题】:accessing struct members in array like manner: padding in structs different than in arrays?以类似数组的方式访问结构成员:结构中的填充不同于数组中的填充?
【发布时间】:2017-06-26 22:46:44
【问题描述】:

假设我有一个包含多个成员的结构,每个成员都属于同一类型。我想通过名称和数组索引安全地访问成员,所以我引入了一个union,它包含实际的struct和一个数组,它对应于struct的成员数量和类型。请参阅以下代码,该代码说明了我想如何使用 union 以两种不同的方式“查看”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 的活动元素以外的任何内容都是未定义的行为。
  • 您可以将operator[] 添加到您的班级。

标签: c++ arrays struct language-lawyer unions


【解决方案1】:

你的方式是 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]; }
};

【讨论】:

  • 我猜data 不需要真正私有,因为我们没有要保护的不变量。无论如何,很好的答案;比我更有建设性。 :)
  • 我同意@BaummitAugen,这应该是公认的答案,比我的和 Baum 的要好。
  • 谢谢。在考虑对齐、填充和此类细节时,我忘记了检查显而易见的事情。结构是由源代码生成器生成的,因此按照您的方法中所述自动生成访问器无关紧要。无论如何,似乎从具有相同对象表示的另一种类型的“透视”“查看”给定对象表示总是会产生 UB;如果这是正确的,那么所有关于陷阱表示的陈述都有什么用?
【解决方案2】:

这里就不用谈内存布局了。

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

所以要明确一点:所有“是否安全/有保证”问题的答案都是“否”。

【讨论】:

    【解决方案3】:

    但它真的有保证吗?

    没有。

    您通过访问union 的非活动成员来调用未定义行为

    【讨论】:

      【解决方案4】:

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

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

      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;
          }
      }
      

      【讨论】:

        猜你喜欢
        • 1970-01-01
        • 2013-01-20
        • 2017-06-11
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 2014-12-30
        • 1970-01-01
        相关资源
        最近更新 更多