c++oding="utf-8" ?>
std::source_location能安全零开销获取文件名、行号、列号和函数名,但必须在宏中调用std::source_location::current()才能捕获真实调用点位置,若仅在log函数默认参数中使用,会记录函数内部行号而非业务代码行号。

直接说结论:std::source_location 能安全、零开销地拿到函数名、文件名、行号和列号,但 它不会自动捕获调用点信息——必须在日志宏里显式构造,且不能跨函数传递。
为什么 std::source_location 不能直接写在 log 函数参数里?
因为 std::source_location::current() 返回的是该函数被调用时的「调用点」位置,不是函数定义处的位置。如果把它作为普通函数参数传入,编译器会在调用 log 函数那一刻才求值,此时得到的是 log 函数内部的行号(比如第 42 行),而不是你真正想记录的业务代码行号(比如 main.cpp 第 105 行)。
常见错误写法:
void log(const char* msg, std::source_location loc = std::source_location::current()) {
printf("%s:%d %s\n", loc.file_name(), loc.line(), msg); // 错!loc 是 log 内部的行号
}
正确做法是把 std::source_location::current() 放到宏里展开:
#define LOG(msg) log(msg, std::source_location::current())
如何用宏 + source_location 构建最小可用日志宏?
宏是唯一能「在调用现场插入 source_location 构造」的机制。注意三点:宏参数要支持表达式、函数名需用 __func__(C++20 未标准化函数名字段)、避免多次求值。
-
std::source_location::current()是 constexpr,无运行时开销,但必须在宏中调用 -
__func__是 C++11 就有的隐式局部变量,比loc.function_name()更可靠(后者在 GCC/Clang 实现不一致,MSVC 可能为空) - 宏里不要对
msg做字符串拼接或流操作,否则可能引发多线程竞争或临时对象生命周期问题
示例(线程安全、无额外拷贝):
#define LOG(msg) do { \
auto _loc = std::source_location::current(); \
fprintf(stderr, "[%s:%d] %s: %s\n", _loc.file_name(), _loc.line(), __func__, msg); \
} while(0)
std::source_location 的 function_name() 为什么经常为空?
这是实现差异导致的:GCC 和 Clang 默认不填充 function_name() 字段,即使加了 -g 也不保证;MSVC 在 /Zi 下可能返回空字符串或 mangled 名。这不是 bug,而是标准只要求「尽力提供」,不作强制。
所以实际工程中:
- 永远别依赖
loc.function_name()输出可读函数名 - 坚持用
__func__(C++11+)或__FUNCTION__(GCC/Clang 扩展)获取当前作用域函数名 - 如果需要完整符号(如带模板参数),得靠外部工具(addr2line、llvm-symbolizer)配合 debug info 解析,
std::source_location不参与
性能与 ABI 兼容性要注意什么?
std::source_location 是纯值类型(trivially copyable),大小通常为 4×uintptr_t(约 32 字节),传值无问题。但它在不同编译器/标准库版本间 ABI 不稳定:
- libstdc++(GCC)和 libc++(Clang)对字段顺序、padding 的处理可能不同
- 如果你的日志库以二进制形式分发(.so/.dll),调用方和库必须用同一套标准库编译
- 跨 DLL 边界传递
std::source_location对象,在 Windows 上尤其危险(MSVC CRT 多版本共存)
稳妥做法:宏定义留在头文件里,所有日志调用都在用户代码侧展开,不通过接口函数透传 std::source_location 实例。
最易被忽略的一点:std::source_location::current() 在内联函数里会展开成内联点位置,不是原始调用点——如果封装了多层日志 wrapper,必须确保最外层宏直接调用 current(),中间层不能用参数转发。
C++免费学习笔记(深入):立即使用
在学习笔记中,你将探索 C++ 的入门与实战技巧!











