std::source_location::current() 必须在函数调用时使用,不能缓存为静态变量,因其是编译期 constexpr 函数,位置信息由调用点决定;正确方式是设为函数默认参数,如 void log(const char* msg, std::source_location loc = std::source_location::current())。

source_location::current() 必须在函数内调用,不能提取到变量里缓存
直接写 static const auto loc = std::source_location::current(); 会记录该变量定义处的位置,而不是调用处 —— 因为 source_location::current() 是编译期求值的 constexpr 函数,展开时机由调用点决定,而非执行点。一旦脱离函数作用域(比如放在全局或 static 变量初始化中),它就固化成那一行的位置。
- 错误写法:
static auto g_loc = std::source_location::current();→ 总是返回该行所在文件/行号 - 正确姿势:必须封装在函数里,每次调用才生成新位置
- 如果想“无宏”,就得接受每次都要显式调用函数,无法像
__FILE__那样零开销注入
不借助宏时,只能靠函数参数传递,且需默认参数 + 调用约定配合
标准库没提供运行时动态捕获位置的 API,source_location 的本质是编译器在调用点自动填充的字面量结构体。所以“无宏”不等于“无调用参与”——你得让编译器知道“这里要填位置”,而唯一方式就是让它看到一个带 std::source_location 默认参数的函数调用。
- 定义函数时写:
void log(const char* msg, std::source_location loc = std::source_location::current()) - 调用
log("hello")时,编译器自动把当前行的file/line/function填进loc - 不能省略参数名:必须是具名参数,否则无法设默认值;也不能用重载替代,因为重载不触发默认实参推导
- 注意:这个机制依赖调用发生点,不是函数定义点;但依然需要一次函数调用,不是“零成本获取”
为什么不用宏就做不到真正的“透明插入”
宏的本质是文本替换,能在语法解析前就把 __FILE__、__LINE__ 这类预处理器符号塞进去;而 source_location::current() 是 C++ 表达式,受语义分析和求值规则约束,必须出现在能形成完整调用表达式的地方。
- 你无法在
if (x) do_something();中间“无声”插入位置信息 —— 没有调用点,就没有current()的落脚处 - 试图用 lambda 捕获?
[loc = std::source_location::current()]{}()→ 还是捕获定义处的位置 - 模板推导?
template<:source_location l> void f()</:source_location>→ 不合法,source_location不是字面量类型(C++20 不支持其作为非类型模板参数)
实际建议:接受“轻量宏”作为合理妥协
所谓“无宏”诉求,往往是为了避免宏污染或调试困难,但完全弃用宏会大幅降低可用性。更务实的做法是封装一层薄宏,只做调用转发,不改变逻辑结构:
#define LOG(msg) log_impl(msg, std::source_location::current()) void log_impl(const char* msg, std::source_location loc);
- 宏名可调试:GDB 能停在
LOG("x")行,展开后也清晰 - 不隐藏控制流:没 if/for/{},只是纯调用
- 比手写
log("x", std::source_location::current())更简洁,且避免漏传 - 若真要彻底去宏,就只能接受每次手动加参数 —— 这在日志、断言等高频场景里,很快就会变成维护负担
真正容易被忽略的是:即使用了 source_location,它的 file_name() 返回的是编译器内部路径(可能含绝对路径或相对路径),不同构建环境输出不一致,别直接用于跨机器日志解析。
C++免费学习笔记(深入):立即使用
在学习笔记中,你将探索 C++ 的入门与实战技巧!











