C++实现类型安全sprintf运行时触发std::bad_alloc异常求助
类型安全sprintf的std::bad_alloc问题排查与解决
问题现象
代码编译无报错,但运行时触发std::bad_alloc异常,报错信息如下:
terminate called after throwing an instance of 'std::bad_alloc' what(): std::bad_alloc zsh: IOT instruction (core dumped) ./printf
原代码如下:
#include <iostream> #include <string> template<class F> struct I { F _f; I(F f):_f(f){} }; template<class F> struct C { F _f; C(F f):_f(f){} }; template<class F> struct S { F _f; std::string _s; S(std::string const& s,F f):_s(s),_f(f) { } }; C c(std::string(".")); S s1(std::string(",Char: "),c); I i(s1); S s2(std::string("Int: "),i); std::string _sprintf(std::string const& prefix,std::string const& str) { return prefix+str; } template<class A> auto _sprintf(std::string const& prefix,I<A> const& a) { return [&](int i){ return _sprintf(prefix+std::to_string(i),a._f);}; } template<class A> auto _sprintf(std::string const& prefix,C<A> const& a) { return [&](char c){return _sprintf(prefix + std::string(1,c),a._f);}; } template<class A> auto _sprintf(std::string const& prefix,S<A> const& a) { return _sprintf(prefix + a._s,a._f); } template<class Fmt> auto sprintf(Fmt const& fmt) { return _sprintf("",fmt); } int main() { auto test = sprintf(s2); std::cout<<test(5)('x'); }
问题根源
悬空引用触发未定义行为:
_sprintf函数返回的lambda使用[&]引用捕获外部变量(如prefix、格式器对象a),但这些变量的生命周期仅局限于当前_sprintf函数调用栈。当lambda被返回并在后续调用时,原变量已被销毁,引用变为悬空状态。访问悬空引用会导致内存访问异常,表现为std::bad_alloc。全局变量类型推导的隐含风险:
原代码中全局变量的模板类型依赖自动推导,虽然编译通过,但类型不明确可能导致后续递归调用时的类型匹配问题,增加调试难度。
修复方案
修改后的代码
#include <iostream> #include <string> template<class F> struct I { F _f; I(F f) : _f(f) {} }; template<class F> struct C { F _f; C(F f) : _f(f) {} }; template<class F> struct S { F _f; std::string _s; S(std::string const& s, F f) : _s(s), _f(f) {} }; // 显式指定模板参数,明确类型 C<std::string> c(std::string(".")); S<decltype(c)> s1(std::string(", Char: "), c); I<decltype(s1)> i(s1); S<decltype(i)> s2(std::string("Int: "), i); std::string _sprintf(std::string const& prefix, std::string const& str) { return prefix + str; } template<class A> auto _sprintf(std::string const& prefix, I<A> const& a) { // 值捕获变量,避免悬空引用 return [=](int i) { return _sprintf(prefix + std::to_string(i), a._f); }; } template<class A> auto _sprintf(std::string const& prefix, C<A> const& a) { // 值捕获变量,避免悬空引用 return [=](char c) { return _sprintf(prefix + std::string(1, c), a._f); }; } template<class A> auto _sprintf(std::string const& prefix, S<A> const& a) { return _sprintf(prefix + a._s, a._f); } template<class Fmt> auto sprintf(Fmt const& fmt) { return _sprintf("", fmt); } int main() { auto test = sprintf(s2); std::cout << test(5)('x') << std::endl; }
关键修改点
- 替换lambda捕获方式:将所有lambda的引用捕获
[&]改为值捕获[=],让lambda内部持有变量的副本,确保后续调用时变量生命周期有效。 - 显式指定模板参数:为全局变量明确指定模板类型,增强代码可读性和类型安全性,避免自动推导带来的潜在问题。
内容的提问来源于stack exchange,提问作者Dragno
相关产品推荐
相关产品推荐

