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



我可以假设两个结构中具有相同类型的两个最前的字段是相同的吗?

我想创建容器类,如果某些元素很小,它将一些元素保留在堆栈中或将它们保留在矢量中。这就像小字符串优化。

我把它发展成类似工会的类,这里描述: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";
}

问题是,由于对齐,当 sizeof stack_data 为 32 且 sizeof heap_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一样吗? 即使实际工会处于heap状态而没有任何腐败,我也可以随时使用stack_data.is_on_stack吗?

我拉了相关的位:

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

M(X) 定义如下:

  • 如果 X 是非联合类类型,则如果 X 没有非静态数据成员,则集合 M(X) 为空; 否则,它由 X 的第一个非静态数据成员的类型(其中所述成员可以是匿名联合)、X0 和 M(X0) 的元素组成。
  • 如果 X 是并集类型,则集合 M(X) 是所有
  • M(Ui) 和包含所有 UI 的集合的并集,其中每个 Ui 是第 i 个非静态的类型 X 的数据成员。
  • 如果 X 是非类类型,则集合 M(X) 为空。

[ 注意:M(X) 是所有非基类子对象的类型集合,这些子对象在标准布局类中保证在 X 中处于零偏移量。

将此应用于您的联合,假设我读得很好,您就会发现 M(您的联合)是stackheapstack.is_on_stackheap.is_on_stack。也就是说,它们都保证处于 0 偏移量。

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

最新更新