c++oding="utf-8" ?>
std::source_location是c++20引入的编译期自动填充结构体,用于替代__file__、__line__等宏,提供文件名、函数名、行号和列号;必须通过current()获取,字段只读,支持默认参数传递,但不可手动构造或跨abi安全传递。

std::source_location 是什么,它能帮你省掉哪些手动填的信息
std::source_location 是 C++20 引入的轻量结构体,用来自动捕获调用点的文件名、函数名、行号和列号。你不用再手写 __FILE__、__LINE__ 这类宏,也不用担心宏展开后位置不准(比如封装在内联函数里时)。它本质是编译期“快照”,调用时由编译器自动填充,不是运行时查栈。
常见错误是把它当成“运行时堆栈追踪”——它只记录调用点,不追溯调用链。如果你需要多层回溯,得靠其他机制(比如 backtrace 或第三方库)。
- 必须用
std::source_location::current()获取,不能手动构造(C++20 标准禁止) - 所有字段都是只读的:
file_name()、function_name()、line()、column() - 注意:MSVC 从 19.30+、GCC 11+、Clang 12+ 才完整支持;老版本会编译失败或返回空字符串
如何在自定义日志/断言函数中传入并格式化 source_location
最典型用法是把 std::source_location 作为默认参数塞进日志函数,避免每个调用都显式传参。它默认值必须是 std::source_location::current(),且必须放在参数列表末尾。
void log_error(const char* msg, const std::source_location& loc = std::source_location::current()) {
fprintf(stderr,
"[%s:%d %s] %s\n",
loc.file_name(), loc.line(),
loc.function_name()[0] ? loc.function_name() : "unknown",
msg
);
}
关键点:
- 参数类型必须是
const std::source_location&,传值会触发拷贝(虽然小但没必要) - 不要用
auto推导默认参数值——编译器不认,会报错 -
function_name()在某些编译器(如 GCC -O2)下可能为空,需判空 fallback - 如果函数内联深度大,
loc记录的是最外层调用点,不是函数内部某行——这是设计使然,不是 bug
和宏组合使用时要注意的陷阱
有人想用宏包裹 log_error 来强制插入位置信息,结果发现宏里调用 std::source_location::current() 拿到的是宏展开位置,而非调用宏的位置——这反而是正确行为,但容易误解。
比如:
#define LOG_ERR(msg) log_error(msg, std::source_location::current())
这样写没问题,但如果你写成:
#define LOG_ERR(msg) do { \
auto loc = std::source_location::current(); \
log_error(msg, loc); \
} while(0)
就多余了,还可能因作用域导致优化问题。更危险的是:
#define LOG_ERR(msg) log_error(msg)
然后指望默认参数生效——不行,宏不会带默认参数过去,loc 会用构造函数默认值(全零/空),不是你想要的调用点。
- 宏里直接调用
std::source_location::current()是安全且推荐的 - 别试图在宏里“预存” location 变量再传,破坏了它的语义初衷
- 如果要用宏做额外处理(比如加时间戳),确保
std::source_location::current()是最后一步求值
性能和 ABI 兼容性要留意什么
std::source_location 本身只有几个 const char* 和整数字段,大小通常为 32–48 字节(取决于字符串长度),按值传递开销不大,但频繁日志场景下仍建议传引用。
真正要注意的是 ABI 稳定性:
- 不同标准库实现对
file_name()返回路径的处理不同:有的给绝对路径,有的给相对路径,有的甚至截断(如 MSVC 默认只保留文件名) - 跨 DLL/so 边界传递
std::source_location要小心——如果两边 stdlibc++/MSVCRT 版本不一致,string_view的二进制布局可能不兼容 - 生产环境开启 LTO 或 whole-program optimization 时,某些编译器可能把
function_name()优化为空,建议始终 fallback 到file_name():line()
调试阶段够用,上线前最好实测你的构建配置下字段是否稳定可读。
C++免费学习笔记(深入):立即使用
在学习笔记中,你将探索 C++ 的入门与实战技巧!











