std::source_location是C++20引入的轻量结构体,用于编译期自动捕获调用点的文件名、行号、函数名和列号;它非运行时反射,不依赖宏或堆栈遍历,字段只读且必须通过current()获取。

std::source_location 是什么,它能捕获哪些信息
std::source_location 是 C++20 引入的轻量结构体,用于在编译期捕获调用点的源码位置(文件、行号、函数名、列号),不是运行时反射,也不依赖调试符号或堆栈遍历。它不包含时间戳或线程 ID,只提供静态源码上下文。
常见误用是把它当成 __FILE__ + __LINE__ 的简单替代——但它比宏更安全、可复制、可传递,且支持默认参数推导。关键字段有:file_name()(const char*)、line()、column()、function_name()(注意:非标准要求实现返回空字符串,GCC/Clang 通常返回,MSVC 在 /Zi 下才可靠)。
如何在日志函数中自动注入 source_location
最实用的方式是把 std::source_location 设为日志函数的**最后一个默认参数**,让调用方完全无感:
void log_info(const char* msg, const std::source_location& loc = std::source_location::current()) {
printf("[%s:%d %s] %s\n",
loc.file_name(), loc.line(),
loc.function_name(), msg);
}调用时直接写 log_info("user logged in"),编译器自动填入当前调用点位置。不需要手动传参,也不用宏包装。
立即学习“C++免费学习笔记(深入)”;
组合式C++代码评审方案,融合静态分析、AI推理、多轮迭代评审和C++专项检查,适用于PR审查、增量代码审查、全项目评审和代码质量评分,触发词包括review cpp、cpp代码评审、C++review、代码审查。
- 必须用
= std::source_location::current()而非= {},否则捕获的是函数定义处而非调用处 - 不能按值传递再转发(如
log_wrapper(loc)),否则捕获的是 wrapper 内部位置;需确保最终日志调用直接使用current()或原样透传 - 若日志函数模板化,
std::source_location参数也需模板化处理,但通常没必要——保持非模板、默认参数最稳
为什么有时 function_name() 返回空或乱码
这是实现差异导致的,不是 bug。C++ 标准只要求 function_name() “尽可能提供”,不保证非空。GCC 和 Clang 在优化级别 -O0 或 -g 下通常返回有效值;但 -O2 -DNDEBUG 可能使内联展开后丢失函数边界,返回空串或 "???"。
实际建议:
- 生产环境别依赖
function_name()做关键路由或诊断,仅作辅助提示 - 若必须显示函数名,可搭配
__func__宏(C++11)一起用:loc.function_name()[0] ? loc.function_name() : __func__ - Clang 14+ 支持
[[clang::no_stack_protector]]等属性影响,但和source_location无关,勿混淆
性能开销和 ABI 兼容性要注意什么
std::source_location 是纯数据结构(通常 4 个 unsigned int 或指针),构造零成本:所有字段在编译期确定,不涉及任何运行时计算或内存分配。它的大小和布局由编译器决定,但跨编译器 ABI 不兼容——比如 GCC 编译的库不能把 source_location 当 ABI 接口暴露给 Clang 调用方。
因此:
- 绝不要在动态库头文件里声明带
std::source_location参数的导出函数(Windows DLL / Linux .so) - 内部模块间使用完全安全,但跨工具链链接时要小心头文件暴露
- 日志宏仍可作为兜底方案(如
#define LOG_INFO(m) log_info(m, std::source_location::current())),但宏会破坏表达式求值顺序,优先用默认参数
真正容易被忽略的是:一旦用了 std::source_location,整个调用链就绑定了 C++20 工具链,连带影响 std::format、概念等其他 C++20 特性——升级前得确认所有依赖项已同步支持。

















