应将std::source_location作为参数传入自定义异常类构造函数并存储为值成员,例如struct tracked_exception : std::runtime_error { std::source_location loc; tracked_exception(const std::string& msg, std::source_location l = std::source_location::current()) : std::runtime_error(msg), loc(l) {} }; 调用时需显式传入std::source_location::current()或依赖默认参数,确保捕获真实抛出点而非封装函数位置。

std::source_location在throw时怎么用才有效
直接在throw语句里构造std::source_location::current()没用——它记录的是throw那一行的位置,不是异常实际抛出点(比如被封装在工具函数里时)。真正要捕获调用者位置,得把std::source_location作为参数传进去。
- 必须显式声明参数,例如
void throw_with_loc(const std::string& msg, const std::source_location& loc = std::source_location::current()) - 不能依赖默认参数自动推导:如果函数内联或被优化,
std::source_location::current()可能指向调用点以外的位置 - 推荐在包装异常类的构造函数中接收
std::source_location,而不是在throw现场临时调用
自定义异常类如何保存和打印位置信息
标准异常类型(如std::runtime_error)不存位置信息,必须自己扩展。关键点是把std::source_location字段存为值(不是引用),并在what()里拼接文件、行号、函数名。
-
std::source_location轻量(通常4个int),可直接作为成员变量值拷贝 - 注意
file_name()返回const char*,不是std::string;function_name()可能为空(取决于编译器和调试信息) - 避免在
what()里做动态分配——有些环境要求what()是noexcept,可用std::array<char></char>预分配缓冲区
struct loc_exception : std::runtime_error {
std::source_location loc_;
loc_exception(const std::string& msg, std::source_location loc = std::source_location::current())
: std::runtime_error(msg), loc_(loc) {}
const char* what() const noexcept override {
// 简化版:实际应避免堆分配
static thread_local std::string buf;
buf = std::string(std::runtime_error::what()) + " @ " +
std::string(loc_.file_name()) + ":" +
std::to_string(loc_.line());
return buf.c_str();
}
};
为什么GCC/Clang下__builtin_FILE()比source_location更可靠
std::source_location在C++20标准里定义行为较松:不同编译器对function_name()和file_name()的实现差异大,尤其启用LTO或模板实例化后,file_name()可能返回空串或临时路径。
- GCC/Clang支持
__builtin_FILE()、__builtin_LINE()、__builtin_FUNCTION(),它们在预处理阶段展开,不受优化影响 - MSVC对应的是
__FILE__、__LINE__、__FUNCSIG__,但__FUNCSIG__含模板签名,长度不可控 - 跨平台宏建议:
#define LOC_INFO std::source_location::current()仅作兜底,生产环境优先用内置宏
catch块里还能拿到原始位置吗
不能。一旦throw完成,原始std::source_location只存在于异常对象内部——除非你把它存进异常类,否则catch时只剩标准异常接口。想在日志里统一打位置,必须提前塞进异常对象。
立即学习“C++免费学习笔记(深入)”;
- 不要指望
std::current_exception()能恢复位置信息:它只捕获异常对象副本,不带额外元数据 - 如果使用
std::exception_ptr跨线程传递异常,确保异常类是noexcept移动构造的,否则位置字段可能失效 - 某些诊断库(如Boost.Stacktrace)会尝试从栈帧还原位置,但依赖调试符号,且不精确到具体行号
最常被忽略的是:std::source_location本身不参与异常对象的std::type_info识别,同一个异常类型的不同实例,哪怕位置不同,catch时也无法按位置分流处理。


















