
std::source_location 是什么,能直接替代 __FILE__ 和 __LINE__ 吗?
std::source_location 是 C++20 引入的标准设施,用于在编译期捕获调用点的源码位置信息。它不是宏,而是一个轻量值类型,包含 file_name()、line()、column() 和 function_name() 四个只读访问函数。它不能直接替代 __FILE__ 和 __LINE__ 的宏展开行为——因为 std::source_location::current() 是一个函数调用,其返回值反映的是该函数被调用处的位置,而非宏定义处。
常见误用是把它写成全局变量或静态常量初始化:
static const auto loc = std::source_location::current(); // ❌ 错!总指向这行
正确做法是让它作为函数参数默认值,在每次调用时自动注入实际调用点信息。
怎么让日志函数自动记录调用位置?
最典型用途是为日志、断言、调试工具注入上下文。关键在于把 std::source_location 设为带默认值的函数参数:
立即学习“C++免费学习笔记(深入)”;
- 默认值必须是
std::source_location::current(),不能加括号以外的任何表达式(如std::source_location{}会固定为定义处) - 参数必须按值传递(它是 trivially copyable,无开销)
- 不能是模板参数推导目标(它不参与重载/推导)
示例:
void log(const char* msg, std::source_location loc = std::source_location::current()) {
fprintf(stderr, "[%s:%d] %s\n", loc.file_name(), loc.line(), msg);
}
// 调用方完全无感知:
log("buffer overflow"); // 自动捕获这一行的文件和行号为什么 function_name() 返回的不是完整签名,且内容不可移植?
function_name() 返回的是编译器生成的符号名(mangled name),标准只要求它“尽可能描述调用点所在函数”,不保证可读性或跨编译器一致。GCC 可能返回 "void foo()",Clang 可能返回 "foo",MSVC 可能返回 。
如果你需要稳定可读的函数名:
- 不要依赖
function_name()做逻辑判断(比如 switch 或 if 比较) - 调试输出可以保留,但日志归类、监控告警等生产场景应避免用它做 key
- 真正需要符号名解析时,应结合外部工具(如 addr2line、llvm-symbolizer)或 ABI 解析库
和 assert / static_assert 有什么关系?
std::source_location 和 assert 没有直接集成,C++23 才引入 std::assertion_handler 支持自定义断言处理,但目前主流标准库(libstdc++、libc++、MSVC STL)的 assert 仍基于传统宏,输出由预处理器决定。
你可以自己封装断言宏来桥接:
#define MY_ASSERT(x) do { \
if (!(x)) { \
log_assert(#x, std::source_location::current()); \
std::abort(); \
} \
} while(0)
void log_assert(const char* expr, std::source_location loc) {
fprintf(stderr, "Assertion failed: %s at %s:%d\n", expr, loc.file_name(), loc.line());
}
注意:宏里调用 std::source_location::current() 是安全的,因为宏展开后它出现在调用点,不是宏定义内部。
真正容易被忽略的是:std::source_location 在 constexpr 函数中无法使用 current()(编译期无法确定“调用点”),所以它只适用于运行时上下文;另外,某些嵌入式或 freestanding 环境可能未实现该特性,需检查 __cpp_lib_source_location 宏。










