std::any实现中unique_ptr的作用及析构未触发问题咨询
关于libcxx中std::any大对象处理函数的异常安全问题
libcxx中std::any的大对象处理函数定义如下:
template <class ..._Args> _LIBCPP_INLINE_VISIBILITY static _Tp& __create(any & __dest, _Args&&... __args) { typedef allocator<_Tp> _Alloc; typedef allocator_traits<_Alloc> _ATraits; typedef __allocator_destructor<_Alloc> _Dp; _Alloc __a; unique_ptr<_Tp, _Dp> __hold(_ATraits::allocate(__a, 1), _Dp(__a, 1)); _Tp * __ret = __hold.get(); _ATraits::construct(__a, __ret, _VSTD::forward<_Args>(__args)...); __dest.__s_.__ptr = __hold.release(); __dest.__h_ = &_LargeHandler::__handle; return *__ret; }
问题
此处用unique_ptr持有原始指针是否为了应对构造函数抛出异常时的安全问题?我编写了如下测试代码,但析构函数完全未被调用:
#include <any> #include <iostream> #include <memory> #include <stdexcept> #include <cstdlib> using namespace std; struct A { double b; double c; double d; void* p; A() { p = malloc(50); std::cout << "constructing" << std::endl; throw std::exception(); } ~A() { free(p); std::cout << "deconstructing " << std::endl; } }; int main() { std::any a; try { __create(a); } catch (std::exception& e) { cout << e.what() << endl; } return 0; }
解答
- unique_ptr的作用确实是异常安全保障
这段代码里用unique_ptr绑定分配的内存和自定义析构器__allocator_destructor,核心目的就是处理构造函数抛出异常的情况:
_ATraits::allocate成功分配内存后,若后续_ATraits::construct(即对象构造)抛出异常,unique_ptr会在函数退出时自动触发析构逻辑,调用__allocator_destructor释放之前分配的内存,避免内存泄漏。- 如果构造成功,就通过
__hold.release()把指针转移给std::any的内部存储,此时unique_ptr不再持有该指针,不会触发释放。
- 测试代码中析构未调用的原因
你的测试代码存在两个关键问题:
- 模板参数缺失:
__create是模板函数,需要明确指定_Tp类型(也就是你的struct A),正确调用应为__create<A>(a)。不指定模板参数的话,编译器无法推导_Tp,实际这段调用可能根本没有正确实例化__create函数,自然不会执行内存分配和构造逻辑。 - 调用私有函数的未定义行为:
__create是libcxx的内部私有函数,直接调用属于未定义行为,不同版本的libcxx可能有不同实现,甚至编译报错。正确的测试方式应该通过std::any的公开接口(比如emplace<A>())触发大对象创建逻辑。
修改后的测试代码(使用公开接口):
#include <any> #include <iostream> #include <stdexcept> #include <cstdlib> using namespace std; struct A { double b; double c; double d; void* p; A() { p = malloc(50); std::cout << "constructing" << std::endl; throw std::exception(); } ~A() { free(p); std::cout << "deconstructing " << std::endl; } }; int main() { std::any a; try { a.emplace<A>(); // 使用std::any的公开接口 } catch (std::exception& e) { cout << e.what() << endl; } return 0; }
运行这段代码就能看到deconstructing的输出,证明异常发生时内存被正确释放了。
内容的提问来源于stack exchange,提问作者zaijun
相关产品推荐
相关产品推荐

