std::source_location 是 c++20 引入的类型安全运行时类类型,自动捕获调用点的文件名、行号、列号和函数名,支持 成员函数 访问,常作带默认值的函数参数。

std::source_location 是类型安全的运行时 对象
std::source_location 是 C++20 引入的 标准库 类型,它在调用点(通常是函数入口)自动构造一个包含文件名、行号、列号和函数名的结构化对象。它不是宏,而是一个可传递、可拷贝、可存储的类类型,支持成员函数如 file_name()、line()、column()、function_name()。
典型用法是作为函数默认参数:
void log(const std::string& msg, const std::source_location& loc = std::source_location::current()) {std::cerr << "[" << loc.file_name() << ":" << loc.line() << "] " << msg << "n"; }
调用 log("Error occurred") 时,编译器自动填入 ** 实际调用处 ** 的位置信息,而非定义处 —— 这对 封装 日志函数至关重要。
__FILE__/__LINE__ 是预处理宏,无类型 、无 作用域 控制
__FILE__ 和 __LINE__ 是传统 C 风格的 预处理器 宏,在预处理阶段被文本替换为字面量 字符串 和整数。它们不经过类型检查,不能直接参与模板推导或作为类成员;且一旦写在某个函数 / 宏定义里,展开后固定指向该定义位置,无法反映调用点。
立即学习“C++ 免费学习笔记(深入)”;
例如:
#define LOG(msg) std::cerr << __FILE__ << ":" << __LINE__ << " " << msg << "n" void foo() { LOG("in foo"); } // 输出的是 LOG 宏定义所在行,不是 foo() 中这行
除非手动传参(如 LOG("msg", __FILE__, __LINE__)),否则容易误标位置 —— 尤其在封装层中极易出错。
关键差异总结
- 时机不同 :source_location 在运行时构造(但开销极小,通常内联为 常量),__FILE__/__LINE__ 在预处理阶段硬编码
- 位置归属不同:source_location 默认捕获调用点,宏展开后常捕获定义点(除非显式传递)
- 表达能力不同:source_location 提供函数名、列号等额外信息,且支持自定义格式化;宏仅提供原始字符串和数字
- 安全性不同:source_location 是 const 对象,不可篡改;宏是裸字面量,易被意外修改或拼接出错(如路径含空格或特殊字符)
现代日志实践中推荐组合使用
不必非此即彼。常见稳健做法是:
- 对外日志 接口 统一用
std::source_location作默认参数,保证调用溯源准确 - 底层输出时,若需兼容旧系统或做特殊处理(如裁剪长路径),再用
loc.file_name()获取字符串进一步加工 - 调试断言中仍可用
assert(condition && "msg")+__FILE__/__LINE__(因 assert 属于预处理行为),但生产级日志建议迁移到 source_location
基本上就这些 —— 不复杂但容易忽略细节。C++20 的 source_location 让“在哪出的问题”这件事,终于有了标准、干净、靠谱的答案。
以上就是