You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

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');
}

问题根源

  1. 悬空引用触发未定义行为:
    _sprintf函数返回的lambda使用[&]引用捕获外部变量(如prefix、格式器对象a),但这些变量的生命周期仅局限于当前_sprintf函数调用栈。当lambda被返回并在后续调用时,原变量已被销毁,引用变为悬空状态。访问悬空引用会导致内存访问异常,表现为std::bad_alloc。

  2. 全局变量类型推导的隐含风险:
    原代码中全局变量的模板类型依赖自动推导,虽然编译通过,但类型不明确可能导致后续递归调用时的类型匹配问题,增加调试难度。

修复方案

修改后的代码

#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

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.06.19 00:20:54