C++对象底层创建/销毁及存储复用的值表示一致性保证问题
类型双关与对象生命周期、存储复用的C++标准疑问
Stack Overflow上经常有关于类型双关操作合法性的问题,我曾参与过其中一个相关讨论,当时产生了一个关于对象生命周期、存储复用后底层字节变化的疑问。
最初的理论示例代码
#include <array> #include <cstddef> #include <cstdint> #include <memory> struct S { alignas(std::uint64_t) alignas(std::uint32_t) std::array<std::byte, sizeof(std::uint64_t)> storage = {some init values}; }; int main() { S s; std::uint64_t* pi64 = std::start_lifetime_as<std::uint64_t>(s.storage.data()); // 隐式在同一位置创建uint32_t对象,结束之前的uint64_t生命周期 std::uint32_t* pi32 = std::launder(reinterpret_cast<std::uint32_t*>(s.storage.data())); // 是否有保证值表示未发生变化? pi64 = std::start_lifetime_as<std::uint64_t>(s.storage); // s.storage是否保证未被修改? }
目前这段代码仅为理论性示例,因为暂无编译器支持std::start_lifetime_as,但它能很好地说明疑问:
定义pi64时,显式创建了一个std::uint64_t对象,其值表示与字节数组中存储的字节内容一致。随后隐式创建了一个重叠的std::uint32_t对象,据我理解这会结束前述std::uint64_t对象的生命周期。此时底层字节会发生什么变化?相关C++标准规则是什么(我知晓unsigned char/std::byte数组有特殊规则)?
修正后的有意义代码示例
后续发现上述片段存在问题,因为std::uint32_t* pi32 = std::launder(reinterpret_cast<std::uint32_t*>(s.storage.data()));并非我之前认为的存储复用,修正后的有意义代码如下:
#include <array> #include <cstddef> #include <cstdint> #include <memory> struct S { alignas(std::uint64_t) alignas( std::uint32_t) std::array<std::byte, sizeof(std::uint64_t)> storage = { some init values}; }; int main() { S s; // 显式在存储位置创建uint64_t对象 std::uint64_t* pi64 = std::start_lifetime_as<std::uint64_t>(s.storage.data()); // 显式复用存储,结束uint64_t的生命周期 new (storage.data()) std::byte[sizeof(std::uint64_t)]; // 隐式在同一位置创建uint32_t对象,结束之前的uint64_t生命周期 std::uint32_t* pi32 = std::launder(reinterpret_cast<std::uint32_t*>(s.storage.data())); // 显式重新在存储位置创建uint64_t对象 // 这是否足以结束uint32_t的生命周期,还是需要像上面那样显式复用存储? pi64 = std::start_lifetime_as<std::uint64_t>(s.storage); // s.storage是否保证未被修改? }
核心问题
问题核心不变,换一种表述:*pi64在存储复用前后是否保证一致?
注:该片段可能仍存在错误,详见第二个std::start_lifetime_as前的注释。若需额外代码行结束std::uint32_t的生命周期,欢迎告知,我会相应修正代码。
内容的提问来源于stack exchange,提问作者Oersted
相关产品推荐
相关产品推荐

