std::source_location在c++20中由编译器在调用点自动注入,需作为带默认值的const引用参数(如loc = std::source_location::current())传递,不可手动构造或在宏内直接调用current(),否则记录位置错误;其function_name()返回mangled名,建议用__func__替代,性能无运行时开销但需注意内联和优化影响。

std::source_location在C++20中怎么获取调用位置
它不是运行时动态捕获,而是编译期由编译器自动注入的结构体,必须作为函数默认参数传入。不写默认值就拿不到,也不能手动构造(std::source_location::current() 是唯一合法构造方式,但仅限当前作用域)。
常见错误是试图在日志宏外直接调用 std::source_location::current(),结果记录的是宏展开后那一行的位置,而非实际调用点:
#define LOG(msg) log_impl(msg, std::source_location::current())
这会导致所有日志都显示在宏定义所在文件的同一行。正确做法是把 std::source_location 作为函数参数带默认值,让编译器在每次调用处填入真实位置:
void log_impl(const char* msg, const std::source_location& loc = std::source_location::current()) { ... }
- 必须用
const std::source_location&,避免拷贝(虽小但标准未保证 trivial) - 不能用
auto推导参数类型,因为不同编译器实现细节可能不同 - Clang/GCC/MSVC 都支持,但 GCC 10+、Clang 11+、MSVC 19.29+ 才稳定,旧版本会静默忽略
如何拼接出带文件名、行号、函数名的格式化日志字符串
std::source_location 提供 file_name()、line()、column()、function_name() 四个成员,但注意:function_name() 返回的是编译器生成的符号名(如 _Z3foov),不是可读的函数名;而 file_name() 可能是绝对路径,也可能含构建目录前缀。
实操建议:
- 用
std::filesystem::path(loc.file_name()).filename().string()提取干净文件名 - 对
loc.function_name()不要直接用,除非你启用了-fno-mangle(不现实),更稳妥是配合宏 +__func__或__FUNCTION__ - 行号和列号直接转
std::to_string(loc.line())即可,无需处理
典型组合示例:
std::ostringstream oss;<br>oss oss <h3>为什么日志里看不到正确的函数名,以及怎么补救</h3><p>因为 <code>std::source_location::function_name()</code> 在 GCC/Clang 下返回的是 mangling 后的符号,MSVC 下才接近可读名。这不是 bug,是标准允许的行为。</p><p>绕过方法只有两个靠谱选择:</p>
- 放弃
function_name(),改用预处理器宏:在日志入口函数加一个const char* func = __func__参数,默认值为__func__,这样既便携又准确 - 若必须用
std::source_location,可在 CMake 中加set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -fno-elide-constructors")并配合调试信息(-g),再用abi::__cxa_demangle解析(仅限 GCC/Clang,且需链接libstdc++)
推荐第一种——简单、零开销、跨平台。例如:
void log_impl(const char* msg, const char* func = __func__,<br> const std::source_location& loc = std::source_location::current());
性能影响和 Release 模式下的注意事项
std::source_location 的填充是编译期完成的,不产生运行时开销,但如果你在日志里频繁调用 std::filesystem::path 或字符串拼接,那才是真正瓶颈。
Release 模式下容易被忽略的点:
- 某些编译器(如旧版 GCC)在
-O2下可能优化掉std::source_location::current()调用,导致字段全为 0 —— 确保启用-g或至少-frecord-gcc-switches - 日志函数若内联(
inline),std::source_location::current()记录的是内联后的位置,不是原始调用点。加[[gnu::noinline]]或__attribute__((noinline))控制 - 避免在循环或高频路径中直接调用带
std::source_location的日志函数,哪怕只是构造参数 —— 编译器仍需生成位置信息,累积起来有指令体积成本
真正在意性能时,应把日志抽象成“延迟格式化”模式:先存 std::source_location 值,等真正需要输出时再解析,而不是每次调用都做 filesystem::path 构造。
C++免费学习笔记(深入):立即使用
在学习笔记中,你将探索 C++ 的入门与实战技巧!











