在 C++ 中定义联合类型标志的最佳方式

The best way to define union type flag in C++

我可以假设连接到 union 的两个结构中类型相同的最前 2 个字段相同吗?

我想创建容器 class 以将一些元素保留在堆栈中(如果它很小)或将它们保留在向量中。类似于小字符串优化。

我像此处描述的类联合 class 一样开发它:http://en.cppreference.com/w/cpp/language/union

并且我使用位域作为类型标志:

#include <iostream>
#include <vector>

struct C {
  bool is_on_stack : 1;
  struct stack_data {
    size_t size : 3;
    int data[(sizeof(std::vector<int>)) / sizeof(int)];
    stack_data() : size(0) {}
  };

  struct heap_data {
    std::vector<int> data;
    heap_data() : data() {}
  };

  union {
    stack_data stack;
    heap_data heap;
  };
  C():stack() {}
  ~C() {
      if(!is_on_stack){
          heap.~heap_data();
      }
  }
};
int main() {
  std::cout << sizeof(C) << "\n";
  std::cout << sizeof(C::stack_data) << "\n";
  std::cout << sizeof(C::heap_data) << "\n";
}

问题是当sizeofstack_data为32而sizeofheap_data为24时,由于对齐,sizeof(C)为40。我在一位字段上多花了 8 个字节!

我发现将标志移动到结构定义中会使所有三种类型的大小都等于 32(标志没有任何额外内存)

union C {
  struct stack_data {
    bool is_on_stack : 1;
    size_t size : 3;
    int data[(sizeof(std::vector<int>)) / sizeof(int)];
    stack_data() : size(0) {}
  } stack;

  struct heap_data {
    bool is_on_stack : 1;
    std::vector<int> data;
    heap_data() : data() {}
  } heap;
};

所以我想知道我可以确定 c.stack_data.is_on_stack 总是与 c.heap_data.is_on_stack 相同吗? 我可以一直使用 stack_data.is_on_stack 即使 heap 状态下的实际联合没有任何损坏吗?

我拉取了相关位:

C++14 标准,第 9 章,第 7 点:

M(X) is defined as follows:

  • If X is a non-union class type, the set M(X) is empty if X has no non-static data members; otherwise, it consists of the type of the first non-static data member of X (where said member may be an anonymous union), X0, and the elements of M(X0).
  • If X is a union type, the set M(X) is the union of all M(Ui) and the set containing all Ui , where each Ui is the type of the ith non-static data member of X.
  • If X is a non-class type, the set M(X) is empty.

[ Note: M(X) is the set of the types of all non-base-class subobjects that are guaranteed in a standard-layout class to be at a zero offset in X. — end note ]

将此应用于您的联盟,假设我读得很好,您会得到 M(您的联盟)是 stackheapstack.is_on_stackheap.is_on_stack。也就是保证都在0偏移量。

顺便说一句,我可能只是在结构中添加一个简单的 is_on_stack,这样您就可以在 进入 之前检查它是哪个 union-ed 类型。尽管在技术上完全相同,但它可能是更清晰的测试 foo.is_on_stack 而不是 foo.heap.is_on_stack.