std::source_location::current() 在宏内调用会固定为宏定义行而非调用行,正确做法是宏显式传入 current() 作为参数,日志函数用默认参数接收以确保捕获调用点位置。

std::source_location 能自动拿到文件名、函数名、行号,但默认构造出来的实例是调用点位置,不是日志宏定义所在位置——这点不注意,所有日志都会显示在宏内部的某一行,完全失去意义。
为什么 std::source_location::current() 在宏里总显示错行?
因为编译器展开宏后,std::source_location::current() 的调用实际发生在宏体内部(比如某行 return std::source_location::current();),而非用户写 LOG_INFO("msg") 的那行。结果所有日志都标着“log.h:42”,而不是你代码里第 105 行那个 LOG_INFO。
解决办法只有一个:让宏在展开时,把调用点的源位置作为参数传进去,而不是在宏内部调用 current()。
- 正确姿势是宏定义里显式把
std::source_location::current()作为默认参数传入日志函数 - 不能在日志函数内部调用
current(),否则位置信息就固化在函数实现处了 - C++20 要求宏必须支持默认参数语法,所以需确保编译器开启
-std=c++20
如何写一个带自动位置信息的日志宏?
核心是宏展开时立即捕获位置,并透传给日志处理函数。示例:
立即学习“C++免费学习笔记(深入)”;
#define LOG_INFO(msg) \
do { \
log_impl((msg), std::source_location::current()); \
} while(0)对应函数声明得匹配这个调用方式:
void log_impl(const char* msg, const std::source_location& loc = std::source_location::current());
注意:函数参数必须带默认值 = std::source_location::current(),否则宏调用无法省略该参数;而宏里显式传入,就覆盖了默认值,确保捕获的是宏调用点,不是函数定义点。
- 别把
std::source_location存为全局或静态变量,它只在构造时有效 - 如果日志函数是模板,记得把
loc参数也作为模板参数推导,避免隐式转换丢失信息 - 某些旧版 Clang(std::source_location::current() 支持不稳定,建议用 GCC 11+ 或 Clang 14+
std::source_location 成员函数返回什么?哪些能直接用?
它提供四个只读访问函数,全部返回 const char* 或 int,无需额外内存分配:
-
loc.file_name():完整路径,如"/home/user/proj/src/main.cpp",Windows 下是反斜杠 -
loc.function_name():C++ 编译器生成的符号名,可能带模板/重载修饰(如"void foo<int>()"</int>),不是原始声明名 -
loc.line()和loc.column():整数,行号从 1 开始,列号通常从 1 开始(但部分编译器可能从 0) - 不要对
function_name()做 strcmp 或格式化假设,不同编译器输出差异大
常见误用:试图用 loc.file_name() 提取文件名而不做路径分割——结果日志里全是长路径,影响可读性。建议封装一个 basename() 辅助函数来截取最后 `/` 或 `\` 后的部分。
性能和 ABI 兼容性要注意什么?
std::source_location 是纯数据结构(三个 const char* + 两个 int),构造开销极小,但频繁取 file_name() 字符串仍涉及指针解引用,不是零成本。
- 如果日志等级被编译期关闭(比如用
if constexpr (level >= INFO)),std::source_location::current()仍会被求值——C++20 标准未规定宏内默认参数是否惰性求值,GCC/Clang 实际都会执行 - 因此高频率日志(如循环内)建议加运行时开关,或改用编译期条件剔除整条宏展开
- ABI 层面它只是 POD,跨动态库传递安全;但若日志库和业务代码用不同标准库实现(如 libstdc++ vs libc++),
function_name()解析行为可能不一致
最常被忽略的一点:std::source_location 不包含时间戳或线程 ID,它只解决“哪来的”问题。想做完整追踪,还得自己组合其他上下文,别指望它一揽子搞定。


















