std::source_location在catch中调用无效,因其返回catch行而非throw行;必须在throw时通过异常类默认参数嵌入,由编译器在抛出点注入位置信息。

std::source_location 用在异常里,能自动记下抛出点位置,但必须嵌入异常对象构造过程,不能靠 catch 时再查 —— 否则拿到的是 throw 行,不是原始错误发生行。
为什么直接在 catch 里调用 std::source_location::current() 没用
常见误解是:在 catch 块里补一句 auto loc = std::source_location::current() 就能定位错误。实际它返回的是 catch 所在行的位置,和真正出问题的地方完全无关。
- 所有异常都显示同一个文件名和行号(即
catch块那行) - 函数名永远是
catch所在函数,不是throw处的函数 - 列号、文件路径等信息全部失效,失去调试价值
正确做法:把 std::source_location 塞进异常构造函数
异常对象必须在 throw 那一刻就捕获位置,靠默认参数触发编译器注入:
class RuntimeError : public std::exception {
public:
RuntimeError(const std::string& msg,
const std::source_location& loc = std::source_location::current())
: message_(msg),
location_(loc) {}
<pre class="brush:php;toolbar:false;">const char* what() const noexcept override { return message_.c_str(); }
const std::source_location& where() const noexcept { return location_; }private: std::string message_; std::sourcelocation location; };
- 抛出时写
throw RuntimeError("buffer overflow");,位置自动填入throw行 - 不能写
throw RuntimeError("...", std::source_location::current());—— 这会固定为异常类定义处或头文件包含行 - 如果异常类型是模板(如
template<typename t> struct Error</typename>),std::source_location::current()仍能正常工作,但需确保模板实例化点就是throw点
搭配 std::format 输出可读错误信息
避免拼接字符串,用 std::format 直接格式化位置字段:
立即学习“C++免费学习笔记(深入)”;
try {
risky_operation();
} catch (const RuntimeError& e) {
std::cerr << std::format("❌ {} [{}:{} in {}]",
e.what(),
e.where().file_name(),
e.where().line(),
e.where().function_name()) << '\n';
}
-
file_name()返回的是相对路径(如"src/utils.cpp"),跨平台时注意不要依赖绝对路径逻辑 -
function_name()是未修饰名("foo"),若需完整签名,得用std::source_location::pretty_name()(部分编译器支持,非标准) - 列号
column()很少用,但对语法分析类工具有用;日志中通常省略
容易被忽略的兼容性坑
不是所有环境都能无痛使用:
- Clang 14+、GCC 12+、MSVC 19.30+ 支持完整特性;旧版本可能只提供空桩实现
- 启用 C++20 模式还不够,必须确认编译器实际实现了
std::source_location—— 可用#ifdef __cpp_lib_source_location守卫 - 静态库中若异常类定义在 .a/.lib 内部,而
throw发生在外部调用方,位置仍准确;但若异常对象在 DLL/so 中构造,某些 Windows MSVC 链接模式下可能丢失调试信息
最麻烦的其实是路径处理:不同编译器对 file_name() 的返回值格式不一致(有带 ./ 的,有全路径的,有仅文件名的),别拿它做字符串匹配或路径解析,只用于展示。


















