C++17前如何规避严格别名规则且不额外占用指针空间?
C++11/14下小对象优化的严格别名规避方案
要在C++17前实现无额外指针开销的小对象优化,核心是绕过严格别名规则对字节数组访问对象的限制,以下是工程上可行的合规方案:
1. 采用带may_alias属性的联合体(GCC/Clang通用方案)
这是GCC标准库实现的核心思路,通过给存储联合体添加编译器扩展属性__attribute__((may_alias)),明确告知编译器:该联合体的存储允许被任意类型别名访问,直接绕过严格别名检查。
修改你的StoragePool实现,将私有存储改为带该属性的联合体:
class StoragePool { public: StoragePool() = default; template <class Tp, class... Args, std::enable_if_t<soo::IsSmallObject<Tp>::value, int> = 0> void emplace(Args&&... args) noexcept(noexcept(Tp(std::forward<Args>(args)...))) { new (storage_.mem_) Tp(std::forward<Args>(args)...); } template <class Tp, class... Args, std::enable_if_t<!soo::IsSmallObject<Tp>::value, int> = 0> void emplace(Args&&... args) { auto tmp = new (storage_.mem_) Tp*; *tmp = new Tp(std::forward<Args>(args)...); } template <class Tp, std::enable_if_t<soo::IsSmallObject<Tp>::value, int> = 0> Tp* get() noexcept { // C++11/14下借助may_alias安全转换 return reinterpret_cast<Tp*>(storage_.mem_); } template <class Tp, std::enable_if_t<!soo::IsSmallObject<Tp>::value, int> = 0> Tp* get() noexcept { return *reinterpret_cast<Tp**>(storage_.mem_); } private: // 带may_alias属性的联合体,规避严格别名规则 union __attribute__((may_alias)) { alignas(soo::small_object_alignment) unsigned char mem_[soo::small_object_size]; void* dummy_ptr; // 确保存储对齐到指针大小,可选 } storage_; };
这个方案完全不额外占用空间,和GCC的std::function实现逻辑一致,在GCC、Clang下兼容C++11/14,且是编译器明确支持的合法扩展。
2. MSVC下的兼容处理
MSVC没有may_alias属性,但它的严格别名检查默认策略更宽松,直接通过reinterpret_cast从字节数组转换到目标类型不会触发未定义行为。可以通过条件编译适配:
#ifdef _MSC_VER // MSVC下直接转换 return reinterpret_cast<Tp*>(storage_.mem_); #else // GCC/Clang用带may_alias的联合体转换 return reinterpret_cast<Tp*>(storage_.mem_); #endif
为什么这些方案可行?
严格别名规则的本质是防止编译器基于类型别名做错误的优化。may_alias属性直接告知编译器,我们会用不同类型访问同一存储,让编译器放弃对应的优化假设,这是标准库实现中常用的合规手段。而MSVC的检查策略本身就允许这种场景的类型转换,无需额外属性。
内容的提问来源于stack exchange,提问作者youtao guo
相关产品推荐
相关产品推荐

