std::shared_ptr触发bad weak_ptr的C++标准规则解析
示例代码
a.h
#pragma once #include <memory> struct AAA; std::shared_ptr<AAA> createAAA();
a.cpp
#include "A.h" struct AAA : public std::enable_shared_from_this<AAA>{}; void destory_AAA(AAA* v) { v->~AAA(); } std::shared_ptr<AAA> createAAA() { auto ret = std::make_shared<AAA>(); // 检查make_shared后shared_from_this是否可用 auto shared_from_ths = ret->shared_from_this(); // #1 此处抛出bad_weak_ptr return shared_from_ths; }
main.cpp
#include "A.h" void destory_AAA(AAA* v); std::shared_ptr<AAA> unused_func(AAA* ptr) { return std::shared_ptr<AAA>{ ptr, destory_AAA }; // #2 } int main() { createAAA(); }
问题根源:模板实例化的一致性要求([temp.res.general] p6)
你怀疑的[temp.res.general] p6条款是解释该问题的正确规则,该条款规定:
模板实体的有效性可在实例化前检查。若出现以下情况,程序格式错误,无需诊断:
- [...]
- 假设实例化中对某构造的解释,与该模板实体任何实际实例化中对应构造的解释不同。
具体分析
#1和#2都会触发std::shared_ptr内部的_Set_ptr_rep_and_enable_shared模板函数实例化:
template <class _Ux> void _Set_ptr_rep_and_enable_shared(_Ux* const _Px, _Ref_count_base* const _Rx) noexcept { // take ownership of _Px this->_Ptr = _Px; this->_Rep = _Rx; if constexpr (conjunction_v<negation<is_array<_Ty>>, negation<is_volatile<_Ux>>, _Can_enable_shared<_Ux>>) { if (_Px && _Px->_Wptr.expired()) { _Px->_Wptr = shared_ptr<remove_cv_t<_Ux>>(*this, const_cast<remove_cv_t<_Ux>*>(_Px)); } } }
两个翻译单元中AAA的类型完整性导致了模板实例化的解释差异:
- 在a.cpp中,
AAA是完整类型,编译器能识别它继承了enable_shared_from_this,因此_Can_enable_shared<AAA>被解析为true,if constexpr分支会被保留,代码会尝试初始化AAA内部的weak_ptr。 - 在main.cpp中,
AAA是不完整类型,编译器无法确定它是否继承enable_shared_from_this,_Can_enable_shared<AAA>被解析为false,if constexpr分支会被丢弃。
根据[temp.res.general] p6,同一个模板实体(_Set_ptr_rep_and_enable_shared<AAA>)在不同翻译单元中的实例化解释不一致,程序属于格式错误无需诊断的情况,MSVC调试模式下抛出的bad_weak_ptr就是这种不一致导致的后果。
ODR规则是否相关?
ODR规则([basic.def.odr] p15)要求同一个实体在所有翻译单元中的定义必须一致,但这里的问题并非std::shared_ptr模板定义违反ODR,而是模板实例化时的上下文差异导致解释不一致,属于模板特有的规则问题,和ODR没有直接关系。
内容的提问来源于stack exchange,提问作者xmh0511
相关产品推荐
相关产品推荐

