std::source_location是c++20引入的轻量结构体,用于编译期捕获调用点的文件名、函数名、行号和列号;gcc 10+、clang 11+、msvc 19.30+支持,需通过默认参数std::source_location::current()传递以确保获取真实调用位置。

std::source_location 是什么,哪些编译器支持
std::source_location 是 C++20 引入的轻量结构体,用于在编译期捕获调用点的文件名、函数名、行号和列号。它不依赖运行时栈回溯,开销极低(本质是几个 const char* 和整数),但必须由编译器在调用处自动填充——你不能手动构造(所有构造函数都是 explicit 且被标记为 constexpr,仅限编译器内部使用)。
GCC 10+、Clang 11+、MSVC 19.30+(VS 2022 17.0)已支持。若用 GCC 9 或 Clang 10 编译却开启 -std=c++20,std::source_location 可能不可用或行为异常——务必检查 __cpp_lib_source_location 宏:
#if __cpp_lib_source_location <h3>如何在函数参数中正确传递 source_location</h3><p>最常见用途是日志或断言辅助函数。关键点:必须用 <code>std::source_location::current()</code> 作为默认参数,且该默认值只能出现在函数声明/定义的参数列表末尾(否则会破坏重载解析):</p><pre class="brush:php;toolbar:false;">void log(const char* msg, const std::source_location loc = std::source_location::current()) {
std::cout <p>以下写法是错的:</p>
- 把
std::source_location放在非末尾参数位置(如void f(int x, std::source_location l = ..., std::string s))→ 编译失败 - 试图手动传入
std::source_location::current()(如log("msg", std::source_location::current()))→ 捕获的是 log 函数内部的调用点,不是调用 log 的那一行 - 用
auto参数推导(如template<typename t> void f(T loc)</typename>)→ 无法触发编译器自动填充,得到的是默认构造值(空字符串 + 0 行号)
file_name() 返回路径的“真实”含义
std::source_location::file_name() 返回的是预处理器看到的 __FILE__ 值——即源文件被包含时的完整路径(可能含相对路径、符号链接展开结果,取决于编译命令中的 -I 和实际 #include 写法)。它不是运行时获取的绝对路径,也不做规范化处理:
- 若编译时用
g++ -I../inc main.cpp,而main.cpp中写了#include "utils.h",且utils.h在../inc/utils.h,那么在utils.h里调用的log()的file_name()会返回"../inc/utils.h",而非/full/path/to/inc/utils.h - Windows 下 MSVC 默认返回短路径(如
C:/PROGRA~1/...),除非启用/FC编译选项 - 若需截取文件名(去掉路径),不能直接用
std::filesystem::path(loc.file_name()).filename().string()——因为file_name()返回的是const char*,且std::filesystem在某些旧标准库实现中可能未完全支持 UTF-8 路径
更稳妥的截取方式(C++20):
std::string_view file_sv(loc.file_name());
auto last_slash = file_sv.find_last_of("/\");
std::string_view basename = (last_slash == std::string_view::npos)
? file_sv : file_sv.substr(last_slash + 1);
性能与调试信息的权衡
std::source_location 本身零运行时开销,但它的字段(尤其是 file_name())指向的是编译器嵌入的字符串字面量,会增大二进制体积。若在高频函数(如内联访问器、循环体内)大量使用,默认参数会强制每个调用点都嵌入一份路径字符串副本。
建议场景:
- 日志、断言、错误报告等低频但需精准定位的场合
- 调试构建中开启,发布构建中通过宏控制是否启用(例如用
#ifdef DEBUG包裹整个参数)
容易被忽略的一点:std::source_location::function_name() 返回的是编译器生成的符号名(mangled),不是原始函数声明名。GCC/Clang 返回类似 "void foo(int)",MSVC 返回 "void __cdecl foo(int)"。若需可读名,仍得依赖调试信息或手写字符串。
C++免费学习笔记(深入):立即使用
在学习笔记中,你将探索 C++ 的入门与实战技巧!











