同一存储空间中混合不同隐式生存期类型对象是否合法?
为实现支持不同简单隐式生存期类型对象的内存池,需要明确:C++的隐式对象创建规则是否允许在同一存储空间内,混合存放不重叠的不同类型对象?
以下是问题示例(为便于阅读省略了安全检查与释放逻辑):
// 两种对齐方式不同的隐式生存期类型 using first_t = std::int16_t; using second_t = std::int64_t; #define DYNAMIC int main() { static constexpr std::size_t Size = 10000; static constexpr std::size_t NbFirst = 3; static constexpr std::size_t NbSecond = 2; #ifdef DYNAMIC auto* storage = new std::byte[Size]; #else alignas(first_t) std::byte storage[Size]; #endif // 在storage位置获取隐式创建的first_t数组 auto* p_first = std::launder(reinterpret_cast<first_t*>(storage)); // 计算剩余存储位置 std::byte* ptr = storage + NbFirst * sizeof(first_t); void* p{ptr}; std::size_t space{Size - NbFirst * sizeof(first_t)}; // 为第二种类型寻找合适的对齐位置 std::align(alignof(second_t), NbSecond * sizeof(second_t), p, space); // 在该位置获取隐式创建的second_t数组:这个数组是否合法存在? auto* p_second = std::launder(reinterpret_cast<second_t*>(p)); for (std::size_t i = 0; i < NbFirst; ++i) { p_first[i] = static_cast<first_t>(i); } for (std::size_t i = 0; i < NbSecond; ++i) { p_second[i] = static_cast<second_t>(i); } }
当前C++标准在[intro.object#13]至[intro.object#16]中定义了隐式对象创建规则,但对部分措辞的解读存在疑问:
疑问1:关于[intro.object#13]的表述
If multiple such sets of objects would give the program defined behavior, it is unspecified which such set of objects is created.
这句话是否意味着只会创建其中一组可能的对象?
疑问2:关于[intro.object#14]和[intro.object#16]的位置约束
These operations select one of the implicitly-created objects whose address is the address of the start of the region of storage, and produce a pointer value that points to that object, if that value would result in the program having defined behavior.
对象是否只能在触发隐式创建操作的起始位置(比如示例中的storage)创建?还是说只要满足对齐等定义良好行为的约束,就可以在存储区域内的任意位置创建?
我倾向于第二种解读,但仍不确定混合不同类型的可行性。因此想明确:上述代码模式是否合法?若不合法,原因是什么?
补充思考:如果该模式不合法,可在第二个位置使用std::start_lifetime_as(若不可用则使用基于std::memmove的实现)作为替代方案。最佳实践应是不依赖隐式对象创建,而是系统性地显式启动对象生存期。
内容的提问来源于stack exchange,提问作者Oersted

