std::source_location 是 C++20 引入的类型安全运行时类类型,自动捕获调用点的文件名、行号、列号和函数名,支持成员函数访问,常作带默认值的函数参数。
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__ 是传统 C 风格的预处理器宏,在预处理阶段被文本替换为字面量字符串和整数。它们不经过类型检查,不能直接参与模板推导或作为类成员;且一旦写在某个函数/宏定义里,展开后固定指向该定义位置,无法反映调用点。
例如:
#define LOG(msg) std::cerr << __FILE__ << ":" << __LINE__ << " " << msg << "\n"
void foo() { LOG("in foo"); } // 输出的是 LOG 宏定义所在行,不是 foo() 中这行
除非手动传参(如 LOG("msg", __FILE__, __LINE__)),否则容易误标位置 —— 尤其在封装层中极易出错。
location 提供函数名、列号等额外信息,且支持自定义格式化;宏仅提供原始字符串和数字不必非此即彼。常见稳健做法是:
std::source_location 作默认参数,保证调用溯源准确loc.file_name() 获取字符串进一步加工assert(condition && "msg") + __FILE__/__LINE__(因 assert 属于预处理行为),但生产级日志建议迁移到 source_location基本上就这些 —— 不复杂但容易忽略细节。C++20 的 source_location 让“在哪出的问题”这件事,终于有了标准、干净、靠谱的答案。