std::source_location 是 C++20 引入的类型安全源位置设施,比 FILE 和 LINE 更可靠:它在调用点捕获文件、函数、行号、列号,避免宏在内联或封装中记录错误位置,且必须作为带默认值的函数参数传入以确保准确性。

std::source_location 是什么,为什么它比 __FILE__ 和 __LINE__ 更好
std::source_location 是 C++20 引入的标准设施,用于在编译期捕获调用点的源码位置(文件、函数、行号、列号)。它不是宏,而是类型安全的结构体,能避免 __FILE__ / __LINE__ 在宏展开或内联函数中“错位”的问题——比如你把日志封装成 inline 函数,传统宏会记录该 inline 函数内部的行号,而 std::source_location::current() 默认捕获的是**调用处**的位置。
关键点:它必须作为函数参数(通常用默认值)传入,不能靠宏“注入”,否则就失去意义。
如何写一个带 source_location 的日志函数
最简可用的日志辅助函数长这样:
void debug_log(const char* msg, const std::source_location loc = std::source_location::current()) {
std::cerr << "[" << loc.file_name() << ":" << loc.line()
<< " in " << loc.function_name() << "] " << msg << "\n";
}调用时完全不用传参:
立即学习“C++免费学习笔记(深入)”;
int main() {
debug_log("starting up"); // 输出类似:[main.cpp:5 in main] starting up
return 0;
}常见错误:
- 把
std::source_location::current()写在函数体内(比如在函数开头手动调用),那记录的就是函数定义处的行号,不是调用点 - 参数没设默认值,又忘记传 —— 编译直接报错,因为
std::source_location没有默认构造函数 - 用
const std::source_location&而非值传递:虽然可行,但没必要;它是个小结构(通常 4 个size_t),按值传更清晰,且避免 lifetime 问题
支持格式化字符串和可变参数的进阶写法
想兼容 printf 风格(如 debug_log("value = %d", x)),得用 C++20 的 std::format:
template<typename... Args>
void debug_log(std::string_view fmt, Args&&... args) {
auto loc = std::source_location::current();
std::cerr << "[" << loc.file_name() << ":" << loc.line() << "] "
<< std::vformat(fmt, std::make_format_args(args...)) << "\n";
}注意点:
- 必须用
std::string_view接收格式串,否则std::vformat无法推导 - 需要编译器开启 C++20(
-std=c++20)且标准库支持std::format(GCC 13+、Clang 15+、MSVC 19.30+) - 如果目标环境不支持
std::format,退回到fmt::format(需链接 {fmt} 库)或用std::ostringstream拼接,但会丢失类型安全
编译与兼容性陷阱
即使写了 C++20 代码,也可能得不到正确行号:
- 某些旧版 libc++(如 macOS 自带)未完全实现
std::source_location,file_name()可能返回空串或 "???" —— 建议用clang++ -stdlib=libc+++ 升级 libc++,或切到 libstdc++ - 启用 LTO(
-flto)时,部分编译器可能优化掉位置信息(尤其在 Release 模式下);调试信息本身不受影响,但source_location是编译期常量,LTO 理论上不该动它,实际表现因编译器版本而异 - Windows 上 MSVC 对
function_name()返回的是修饰名(mangled name),不是可读函数名;若需可读名,得额外调用UnDecorateSymbolName,代价高,一般只在诊断工具里做
最稳的做法:开发阶段用 -O0 -g -std=c++20,确保 source_location 行为可预测;上线前若关调试信息,至少保留 file_name() 和 line(),它们不依赖 debug info。



















