std::source_location 是 C++20 引入的安全替代 FILE 和 LINE 的类型,需通过 std::source_location::current() 显式获取调用点信息,不能默认构造;须配合默认参数函数与内联包装宏使用,支持编译期零开销但无法运行时关闭。
std::source_location 是什么,它能直接替代 __FILE__ 和 __LINE__ 吗?
能,但不是“直接替换”——
是 C++20 引入的类型,封装了调用点的文件名、函数名、行号、列号,比宏更安全、更可控。它不依赖预处理器展开,不会污染宏命名空间,也不会在内联或模板实例化时错位。
常见错误是以为它会自动捕获调用位置:其实它默认构造时只记录定义点(比如函数体内部),必须显式用
才能获取调用者位置。
错误写法:
正确写法:
注意:参数必须设为默认值,且不能是 const 引用(否则无法绑定临时对象)
如何在日志宏里安全注入 std::source_location?
宏本身不能直接传入
(C++20 允许,但部分老编译器不支持;且宏展开后可能被优化掉)。稳妥做法是用内联函数包装 + 宏转发。
典型结构:
立即学习
“
C++免费学习笔记(深入)
”;
这样既避免宏参数求值顺序问题,又确保
在宏调用点求值。
C知道
CSDN推出的一款AI技术问答工具
下载
不要写
—— 某些 Clang 版本在 -O2 下可能把
提前到宏定义处求值
如果需要格式化(如
),得用可变参数模板,而非可变参数宏(后者无法透传
)
MSVC 19.30+、GCC 11+、Clang 12+ 支持良好;GCC 10 不支持
std::source_location::file_name() 返回的路径是绝对还是相对?怎么统一成文件名?
完全取决于编译器和构建系统——
返回的是编译器传入的原始路径字符串,可能是绝对路径(如
),也可能是相对路径(如
),甚至带 . 前缀(Windows 上常见)。
没有标准库函数切分路径,必须自己处理。最轻量的做法是找最后一个
或
:
别用
—— 它会分配内存、抛异常,不适合高频日志路径提取
注意:某些构建系统(如 Bazel)可能生成带 execroot 的长路径,
仍可靠
Clang 在 header unit 模式下可能返回空字符串,需判空
性能开销有多大?能不能关掉?
几乎为零:
是编译期常量注入,不产生运行时函数调用。所有字段(
,
等)都是 constexpr 字符串字面量或整数字面量的引用,无内存分配、无字符串拷贝。
但它无法“关闭”——只要函数签名含默认
参数,编译器就必须在每次调用时填入该位置信息。真正控制开关得靠宏条件:
Release 构建中若完全不需要源码信息,去掉默认参数比运行时判断更彻底
不要试图用
在函数体内跳过日志输出——位置信息已注入,省不掉
注意:即使不打印,
对象本身仍占 8~16 字节栈空间(取决于字段数量),对极端栈敏感场景(如中断 handler)要留意
最容易被忽略的一点:
的
在 GCC 和 Clang 下返回的是 mangled 名,不是可读函数名;若需可读名,得额外接 cxxabi 解析——这反而带来运行时开销和依赖,通常不如直接用宏
更实际。
std::source_locationstd::source_location::current()void log(const char* msg) { auto loc = std::source_location{}; /* 这里记录的是 log 函数自身位置 */ }void log(const char* msg, std::source_location loc = std::source_location::current()) { /* loc 记录的是调用 log() 的那行代码位置 */ }std::source_location::current()inline void real_log(const char* msg, std::source_location loc = std::source_location::current()) {
fprintf(stderr, "[%s:%d] %s
", loc.file_name(), loc.line(), msg);
}
#define LOG(msg) real_log(msg)std::source_location::current()#define LOG(msg) real_log(msg, std::source_location::current())current()LOG("x=%d", x)std::source_locationstd::source_locationfile_name()/home/user/proj/src/log.cppsrc/log.cpp/auto basename(const char* path) -> const char* {
const char* p = strrchr(path, '/');
if (!p) p = strrchr(path, '\');
return p ? p + 1 : path;
}
// 使用:loc.file_name() → basename(loc.file_name())std::filesystem::pathstrrchrstd::source_location::current()file_name()line()std::source_location#ifdef ENABLE_SOURCE_LOCATION
# define LOG(msg) real_log(msg)
#else
# define LOG(msg) real_log_no_loc(msg)
#endifif constexprstd::source_locationstd::source_locationfunction_name()__func__