为何std::allocate_shared()可对未构造对象使用std::addressof()?
根据cppreference中std::to_address()的注释:
即使p指向的存储空间中尚未构造对象,仍可使用std::to_address();这种情况下无法使用std::addressof(*p),因为std::addressof()的参数没有可绑定的有效对象。
但在LLVM libcxx的allocate_shared实现代码中:
// libcxx/include/__memory/shared_ptr.h template <class _Tp, class _Alloc, class... _Args, __enable_if_t<!is_array<_Tp>::value, int> = 0> _LIBCPP_HIDE_FROM_ABI shared_ptr<_Tp> allocate_shared(const _Alloc& __a, _Args&&... __args) { using _ControlBlock = __shared_ptr_emplace<_Tp, _Alloc>; using _ControlBlockAllocator = typename __allocator_traits_rebind<_Alloc, _ControlBlock>::type; __allocation_guard<_ControlBlockAllocator> __guard(__a, 1); ::new ((void*)std::addressof(*__guard.__get())) _ControlBlock(__a, std::forward<_Args>(__args)...); auto __control_block = __guard.__release_ptr(); return shared_ptr<_Tp>::__create_with_control_block( (*__control_block).__get_elem(), std::addressof(*__control_block)); }
在placement new调用时,__guard.__get()指向的存储空间中还未构造_ControlBlock对象,却使用了std::addressof(*__guard.__get()),这看起来和cppreference的注释矛盾,这是为什么?是我的理解有误吗?
这里的核心是对C++标准中指针解引用与对象生命周期边界的细节理解,以及std::addressof的实现特性:
解引用未构造对象的特殊允许场景:C++标准并非完全禁止解引用指向未构造对象的指针。只要不对解引用得到的“伪对象”执行任何依赖对象生命周期的操作(比如调用成员函数、访问非静态成员、触发虚函数机制等),仅做取地址这类无副作用的操作是被允许的。
std::addressof的本质:
std::addressof的设计目的是绕过对象重载的operator&,直接获取对象的真实内存地址。它的实现不依赖对象的构造状态——既不会访问对象的成员,也不会触发任何对象生命周期相关的行为。std::addressof(*p)本质上就是在获取指针p指向的内存起始地址,和直接使用static_cast<void*>(p)的效果一致,只是语法上通过解引用再取地址的方式实现。cppreference注释的语境:cppreference的注释是在强调,不能将
*p绑定到需要有效对象的引用场景(比如传入一个要求T&参数的函数),或者对*p执行任何需要对象已构造的操作。但std::addressof的参数是模板化的引用,它对这个“伪对象”的处理仅停留在取地址层面,完全符合标准允许的操作范围。libcxx实现的合规性:这段代码是符合C++标准的。
__guard.__get()返回的指针指向的是已经分配好、且对齐正确的内存区域,解引用后仅用于取地址,没有触发任何对象生命周期的操作,因此是安全合法的。
内容的提问来源于stack exchange,提问作者ValueError

