在 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(您的联盟)是 stack
、heap
、stack.is_on_stack
、heap.is_on_stack
。也就是保证都在0偏移量。
顺便说一句,我可能只是在结构中添加一个简单的 is_on_stack
,这样您就可以在 进入 之前检查它是哪个 union
-ed 类型。尽管在技术上完全相同,但它可能是更清晰的测试 foo.is_on_stack
而不是 foo.heap.is_on_stack
.
我可以假设连接到 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(您的联盟)是 stack
、heap
、stack.is_on_stack
、heap.is_on_stack
。也就是保证都在0偏移量。
顺便说一句,我可能只是在结构中添加一个简单的 is_on_stack
,这样您就可以在 进入 之前检查它是哪个 union
-ed 类型。尽管在技术上完全相同,但它可能是更清晰的测试 foo.is_on_stack
而不是 foo.heap.is_on_stack
.