std::source_location需编译器支持c++20并显式启用标准(-std=c++20或/std:c++20),头文件为;日志函数须将其作为默认参数以获取调用点位置;file_name()返回完整路径,建议用std::filesystem::path提取文件名;function_name()依赖调试符号,可fallback至__func__;注意内联函数可能导致位置信息偏移。

std::source_location在C++20中怎么启用
必须确认编译器支持C++20且开启对应标准,否则std::source_location不可用。GCC 10+、Clang 12+、MSVC 19.30+ 支持,但默认不启用C++20——需显式加-std=c++20(GCC/Clang)或/std:c++20(MSVC)。若编译报错‘source_location’ is not a member of ‘std’,八成是标准没设对。
头文件只需包含<source_location></source_location>,无需额外宏定义或链接选项。
如何把source_location塞进日志函数参数
不能靠宏自动传参——std::source_location::current()必须在调用点求值,所以日志函数得把std::source_location作为**默认参数**,让编译器在每次调用处内联生成位置信息。
- 错误写法:
void log(const char* msg) { auto loc = std::source_location::current(); ... }→ 所有调用都返回日志函数自身的文件/行号 - 正确写法:
void log(const char* msg, const std::source_location& loc = std::source_location::current()) - 推荐用
std::string_view接收消息,避免隐式构造临时std::string
示例:
void log(std::string_view msg, const std::source_location& loc = std::source_location::current()) {
fprintf(stderr, "[%s:%d %s] %s\n",
loc.file_name(), loc.line(), loc.function_name(), msg.data());
}
为什么file_name()返回的路径可能很长或含相对路径
std::source_location::file_name()返回的是预处理器展开后的完整路径(如/home/user/project/src/main.cpp),不是你期望的main.cpp。不同编译器行为略有差异:Clang 默认给绝对路径,GCC 可能受-frecord-gcc-switches影响,MSVC 在构建时依赖/FC开关。
实际日志中通常要截取文件名,别硬编码strrchr——用std::filesystem::path更可靠(C++17起):
auto filename = std::filesystem::path(loc.file_name()).filename().string();
注意:std::filesystem需链接-lstdc++fs(GCC)或启用/std:c++17及以上(MSVC),且某些嵌入式或旧环境不支持。
性能开销和调试符号依赖
std::source_location本身是轻量结构体(通常4个const char* + 2个int),构造开销几乎为零——所有字段在编译期确定,运行时只是读内存。但function_name()返回的字符串依赖调试信息:GCC/Clang 需要-g,MSVC 需要/Zi或/Z7;否则function_name()可能为空或返回mangled名(如_Z3foov)。
发布构建中若禁用调试符号,又想保留函数名,可考虑配合__func__宏作fallback:
const char* func_name = loc.function_name()[0] ? loc.function_name() : __func__;
不过__func__不带命名空间和参数签名,语义不如function_name()精确。
真正容易被忽略的是:一旦把std::source_location作为参数传入模板函数或内联函数,编译器可能因优化而将current()提升到外层调用点——结果位置信息“跳级”了。遇到奇怪的行号偏移,先检查是否跨了内联边界。
C++免费学习笔记(深入):立即使用
在学习笔记中,你将探索 C++ 的入门与实战技巧!











