不能。std::source_location不提供类名,仅含文件、函数、行号、列号;function_name()返回编译器符号名,需手动解析如查找最后一个“::”后括号前的子串,但受编译器差异、lambda、限定符等影响,不可靠;推荐日志宏中仅传递source_location,延迟解析或显式传入类名。

std::source_location能自动获取类名吗
不能。std::source_location 只提供文件名、函数名、行号和列号,function_name() 返回的是编译器生成的符号名(如 void MyClass::do_something()),不是干净的类名。它不解析作用域,也不剥离命名空间或模板参数。
如何从__func__或source_location::function_name()中提取类名
需要手动解析函数签名字符串。常见做法是:在 function_name() 返回值中查找 ::,取最后一个 :: 之后、括号 ( 之前的部分。但要注意:
- 普通函数没有
::,此时类名为空 - lambda 表达式可能返回类似
operator() const [with T = int],需跳过 - MSVC 和 GCC/Clang 对
function_name()的格式不一致(MSVC 带public:前缀,Clang 可能含const、&&等限定符) - 建议只对成员函数做解析,先检查字符串是否含
::且不含[with(lambda 标识)
示例片段:
std::string extract_class_name(const std::string& func) {
size_t pos = func.rfind("::");
if (pos == std::string::npos || func.find("[with", pos) != std::string::npos) return {};
size_t end = func.find('(', pos);
return func.substr(pos + 2, end == std::string::npos ? std::string::npos : end - pos - 2);
}日志宏里怎么安全集成source_location和类名提取
直接在宏里调用 extract_class_name 会有两个问题:一是 std::source_location::current() 在宏展开时捕获的是宏定义处而非调用处的位置;二是字符串解析在日志热点路径上可能影响性能。推荐方案:
立即学习“C++免费学习笔记(深入)”;
组合式C++代码评审方案,融合静态分析、AI推理、多轮迭代评审和C++专项检查,适用于PR审查、增量代码审查、全项目评审和代码质量评分,触发词包括review cpp、cpp代码评审、C++review、代码审查。
- 宏必须用
std::source_location::current()作为默认参数,确保捕获调用点信息 - 类名提取逻辑延迟到日志真正格式化时(比如在 logger 后端),避免每次打点都解析
- 若必须在前端提取,加一层
static constexpr或缓存机制(但注意多线程下std::string构造非 trivial) - 更稳妥的做法:让使用者显式传入类名(如
LOG_INFO(MyClass, "msg")),避免运行时解析不确定性
典型安全宏写法:
#define LOG_DEBUG(msg, ...) \
do { \
auto loc = std::source_location::current(); \
logger.debug(loc, __VA_ARGS__, msg); \
} while(0)然后在 logger.debug 成员函数里统一处理 loc.function_name() 提取。
为什么不该依赖 typeid(T).name() 来补全类名
因为 typeid 需要具体类型实例,而日志常发生在自由函数、静态成员或模板上下文中,无法可靠获得当前类的 T。即使有 this 指针,typeid(*this).name() 返回的是动态类型(可能为派生类),且名称是实现定义的(需 abi::__cxa_demangle 解码),不可靠、不可移植、开销大。
真正稳定的类名来源只有两处:源码中写死的标识(如宏参数)、或从 function_name() 中保守解析——后者虽不完美,但至少与编译器生成的符号一致,且不引入 RTTI 依赖。
实际项目里,多数成熟日志库(如 spdlog、g3log)都回避自动类名推导,原因就在这里:边界 case 太多,不如交给用户控制。

















