std::source_location::current() 返回日志函数自身位置,因其在函数体内调用时“直接调用者”即该函数;正确做法是将其作为带默认值的参数,由编译器在调用点自动注入调用位置。

不用手写 __FILE__、__LINE__,std::source_location 能自动捕获调用点位置,但必须在函数参数中声明为默认值,且不能跨编译单元隐式传递。
为什么 std::source_location::current() 总是返回日志函数内部的位置?
这是最常见误解:直接在日志函数体里调用 std::source_location::current(),它捕获的是该函数自身的定义位置,而非调用它的那行代码。C++ 标准要求 current() 返回「调用该函数的源码位置」,但它只对**直接调用者**生效——而如果你把 current() 写在函数体内,那“调用者”就是你自己。
正确做法是把它作为函数参数,默认值由编译器在调用点注入:
void log_info(const char* msg,
const std::source_location loc = std::source_location::current()) {
std::cout << loc.file_name() << ":" << loc.line()
<< " - " << msg << "\n";
}- 必须用默认参数形式,不能提前计算再传入
- 不能在 lambda 内部直接调用
current()期望得到外层调用位置(lambda 是新函数体) - 模板函数中同样适用,但需确保调用点可见(头文件内联或显式实例化)
哪些编译器和标准模式支持?
std::source_location 是 C++20 特性,但支持程度有差异:
立即学习“C++免费学习笔记(深入)”;
- Clang 13+ 和 GCC 12+ 在
-std=c++20下完整支持 - MSVC 19.30+(VS 2022 17.0+)支持,但早期版本(如 19.29)对
file_name()返回空字符串 - 如果用
-std=c++17编译,即使头文件包含成功,调用current()也会触发编译错误,不是运行时失效 - 注意:某些嵌入式工具链(如 ARM GCC 11)尚未实现,会报
‘source_location’ is not a member of ‘std’
和宏方案比,std::source_location 有什么实际代价?
它不是零开销抽象,但比传统宏更可控:
- 每个默认参数实例会增加约 24–32 字节栈空间(含
file_name字符串指针、line、column、function_name) - 字符串内容来自编译器生成的只读段,不涉及动态分配,但
file_name()返回的是const char*,指向编译期确定的路径字面量 - 宏方案(如
#define LOG(x) do { printf("%s:%d ", __FILE__, __LINE__); ... })无函数调用开销,但无法被 IDE 跳转、会被预处理器展开破坏作用域、且无法参与重载或模板推导 - 混合使用时注意:不要在宏里调用带
source_location参数的函数,否则位置信息仍指向宏展开处,而非原始调用行
如何避免日志位置信息被优化掉?
Release 模式下,部分编译器可能省略 source_location 的字段填充,尤其是 function_name() 或 file_name() 为空。这不是 bug,而是优化行为:
- GCC 需加
-g(即使不调试),否则file_name()可能返回空 - Clang 默认保留,但若用了
-frecord-gcc-switches类似精简调试信息选项,也可能丢失 - MSVC 需启用
/Zi或/ZI,仅/Z7不够稳定 - 验证方法:在日志函数内加一句
volatile auto test = loc.file_name();,防止编译器完全丢弃整个对象(仅调试用,勿留生产)
真正难处理的是构建系统级路径裁剪——比如 file_name() 返回 /home/user/project/src/log.cpp,而你只想显示 src/log.cpp。这得靠运行时字符串截取或构建时预定义宏配合,std::source_location 本身不提供相对路径能力。


















