std::source_location::current()不能直接用在日志函数内,因为其返回值固定为定义点位置;必须作为默认参数或在宏中调用,才能捕获真实调用点的文件名、行号等信息。

std::source_location::current() 为什么不能直接用在日志宏里?
因为 std::source_location::current() 是一个编译期常量表达式,但它的值必须由调用点(而非定义点)生成——如果你把它写死在日志函数内部,所有调用都会返回那个函数自身的文件/行号,而不是实际调用日志的地方。
典型错误写法:
void log(const char* msg) {
auto loc = std::source_location::current(); // ❌ 总是返回 log() 函数的定义位置
printf("[%s:%d] %s\n", loc.file_name(), loc.line(), msg);
}
正确做法是把 std::source_location::current() 放进宏里,在预处理阶段“快照”调用现场:
#define LOG(msg) do { \
auto _loc = std::source_location::current(); \
printf("[%s:%d] %s\n", _loc.file_name(), _loc.line(), msg); \
} while(0)
如何让 source_location 和可变参数日志函数一起工作?
C++20 要求 std::source_location 参数必须是默认实参,且只能放在参数列表末尾;否则无法和 ... 兼容。你不能靠重载或模板推导自动塞进去。
常见失败尝试:
- 写成
template<typename... args> void log(Args&&... args, std::source_location loc = std::source_location::current())</typename...>→ 编译失败:默认参数不能出现在包展开之后 - 试图在函数内调用
std::source_location::current()→ 返回的是函数体位置,不是调用点
可行方案只有两种:
- 用宏包裹函数调用,显式传入
std::source_location::current() - 定义函数时把
std::source_location作为**最后一个非包参数**,并设默认值:
template<typename... args>
void log(const char* fmt, Args&&... args,
const std::source_location loc = std::source_location::current()) {
printf("[%s:%d] ", loc.file_name(), loc.line());
printf(fmt, std::forward<args>(args)...);
}</args></typename...>
调用时写 log("value = %d", 42) 即可,loc 自动捕获调用点。
组合式C++代码评审方案,融合静态分析、AI推理、多轮迭代评审和C++专项检查,适用于PR审查、增量代码审查、全项目评审和代码质量评分,触发词包括review cpp、cpp代码评审、C++review、代码审查。
Windows MSVC 和 GCC 对 source_location 的兼容性差异
MSVC 19.30+(VS 2022 17.0+)支持完整语义,但早期版本(如 19.29)会把 file_name() 返回为相对路径甚至空字符串;GCC 11+ 基本可用,但 function_name() 在 -O2 下可能被优化掉(返回空或 "unnamed")。
关键事实:
-
file_name()和line()在所有主流编译器上都可靠 -
function_name()不是标准强制要求,GCC/Clang 默认不填,MSVC 填但不保证稳定 - 调试信息开启与否(-g / /Zi)不影响
source_location,它纯靠编译器内置机制生成
别依赖 function_name() 做逻辑分支,只用于日志展示即可。
宏里用 source_location 时容易忽略的两个细节
一是宏展开后可能破坏作用域或引发逗号表达式歧义,比如 if (cond) LOG("x"); else ... 如果宏没用 do {...} while(0) 包裹,else 会绑定错分支。
二是 std::source_location::current() 在宏中多次调用会被多次求值——虽然开销极小,但理论上不是“单次快照”。如果后续还要用同一个 loc 做多次格式化(比如同时打到文件和 stderr),务必缓存一次:
#define LOG(fmt, ...) do { \
const auto _loc = std::source_location::current(); \
fprintf(stderr, "[%s:%d] " fmt "\n", _loc.file_name(), _loc.line(), ##__VA_ARGS__); \
} while(0)
注意 const auto 和 do/while(0) 这两处,少一个都可能在复杂条件或模板上下文中出问题。
C++免费学习笔记(深入):立即使用
在学习笔记中,你将探索 C++ 的入门与实战技巧!










