std::source_location是C++20零开销定位工具,需显式传参或宏封装才能获取真实调用点;直接调用current()返回其自身位置,而非调用者位置。

直接说结论: std::source_location 是 C++20 引入的轻量级工具,能零开销获取调用点的文件名、行号、函数名和列号;但它不是“自动记录位置”的魔法,必须显式传参或用宏封装,否则拿不到你想要的位置。
为什么直接写 std::source_location::current() 常常不准?
很多人一上来就写:auto loc = std::source_location::current();,结果发现文件名是头文件路径、行号是模板实例化点——因为 current() 返回的是它**被调用的那一行**的位置,而不是调用它的函数的调用点。
常见错误现象:
- 在封装的日志函数里直接调用
current(),结果日志总显示日志函数自身的行号 - 在模板函数里用
current(),行号指向实例化处(比如 main.cpp 第 12 行),而非模板定义处
正确做法是把 std::source_location 当作函数参数,默认值由调用方自动填充:
立即学习“C++免费学习笔记(深入)”;
void log(const char* msg, std::source_location loc = std::source_location::current()) {
printf("[%s:%d] %s\n", loc.file_name(), loc.line(), msg);
}这样调用 log("hello") 时,编译器自动把调用点(比如 main.cpp:42)填进 loc。
宏封装是绕过语言限制的必要手段
C++ 不支持“在函数参数默认值中捕获调用点上下文”,所以想实现类似 LOG("msg") 这种简洁调用,必须靠宏。宏展开发生在预处理阶段,能确保 __FILE__ 和 __LINE__ 指向真实调用位置。
安全封装要点:
- 用
std::source_location替代传统宏里的__FILE__/__LINE__,语义更清晰、类型安全 - 避免宏展开后参数被多次求值(比如
LOG(x++)),应把表达式先存到 const 变量再传入 - 推荐写法(带命名空间保护):
#define LOG(msg) ::detail::log_impl((msg), std::source_location::current())
namespace detail {
void log_impl(const char* msg, std::source_location loc);
}
std::source_location 的性能和兼容性事实
它不分配内存、不抛异常、不依赖 RTTI,所有字段(file_name()、line() 等)都是编译期常量,运行时只是读几个字节——真正零开销。
但要注意:
- MSVC 19.29+、GCC 10+、Clang 11+ 才完整支持;旧版本可能只提供空桩实现(返回空字符串/0)
-
file_name()返回const char*,内容生命周期绑定到编译单元,不能strdup或长期持有指针 - 没有标准方式获取相对路径(如
src/main.cpp),file_name()通常是绝对路径,需自行截断
最容易被忽略的一点:std::source_location 的字段值完全由编译器注入,你无法构造或修改它;哪怕自己写个结构体模仿,也无法触发编译器自动填充逻辑——必须用 std::source_location::current() 或显式传参,别试图“手动生成”。

















