std::source_location是c++20标准库中用于编译期捕获调用点文件名、行号、函数名和列号的类型安全值,可传参、存储和序列化,替代易出错的__file__和__line__宏。

std::source_location 是什么,为什么值得换
它是一个 C++20 标准库类型,用来在编译期捕获调用点的文件名、行号、函数名和列号。不是宏,是类型安全、可拷贝、可传参的值——这意味着你能把它当参数传进函数、存在日志结构体里、甚至序列化,而 __FILE__ 和 __LINE__ 只能在宏展开那一刻“快照”,一离开宏作用域就失效。
怎么用 std::source_location 替代 __FILE__ 和 __LINE__
核心是配合默认参数 + 内联函数,让编译器自动填入调用点信息:
void log_error(const char* msg, const std::source_location loc = std::source_location::current()) {
printf("[%s:%d] %s\n", loc.file_name(), loc.line(), msg);
}调用时完全不用传 loc 参数:
log_error("buffer overflow"); // 自动捕获这一行的位置- 必须用
std::source_location::current()作默认值,不能写成{}或省略,默认构造出来的值是未定义行为 - 函数不能是纯模板(比如
template<typename t> void f()</typename>),否则current()捕获的是模板实例化点,不是调用点;加inline或非模板函数才可靠 - 如果函数被内联优化掉,
current()仍正确;但若函数被链接时优化(LTO)或跨 TU 调用,某些旧编译器(如 GCC 11 前)可能回退到定义点而非调用点
和宏相比,哪些地方容易翻车
看似更现代,但几个隐性坑比想象中多:
立即学习“C++免费学习笔记(深入)”;
-
std::source_location::file_name()返回const char*,指向只读字符串,但它的生命周期绑定到source_location对象本身——别存指针、别跨函数返回裸指针 - Clang 14+ 和 GCC 12+ 支持
function_name(),但 MSVC 2022 17.5 前返回空串;若依赖函数名,得加#ifdeffallback - 调试信息缺失时(如 Release 无 debug info),
file_name()可能返回空或 "",而 __FILE__总是字面量字符串 - 不能直接用于宏展开上下文:比如你想在
assert里用它,得先包一层内联函数,没法像__FILE__那样塞进宏定义体
性能和兼容性要盯住哪几条线
开销极小,但得看编译器和标准库实现:
- GCC 12/Clang 14 下,
std::source_location::current()编译为几条 mov 指令,和宏一样零运行时成本 - libstdc++ 和 libc++ 都已支持,但 MSVC 的
std::source_location在 /std:c++20 下才启用,且早期版本(column() 返回 0 - 如果你的项目还要支持 C++17,不能全局替换——得保留宏 fallback,比如:
#if __cpp_lib_source_location >= 201907L auto loc = std::source_location::current(); #else auto loc = make_location(__FILE__, __LINE__); #endif
真正麻烦的不是语法,而是团队里有人顺手把 source_location 当普通 struct 成员存进 long-lived 对象,忘了它内部指针不管理内存;或者在跨平台构建时,某台机器的 Clang 版本卡在 13,结果 function_name() 全是空。这些细节不报错,但日志一跑就丢关键上下文。










