发布于2026-07-17 阅读(0)
扫一扫,手机访问
直接说结论:C++20 用 std::source_location 最可靠;老标准只能靠预定义宏(__func__、__FILE__、__LINE__),但这些宏本质上是编译器注入的静态字符数组,既不是函数调用,也不能传参或延迟求值,稍不注意就踩坑。下面展开聊聊,到底怎么回事。
__func__ 不是标准函数,也不能取地址__func__ 是编译器在每个函数作用域内自动注入的静态字符数组,比如 static const char __func__[] = "foo";。它本质上是个隐含的局部变量,但它的行为很特殊——不能参与常规的表达式求值时机控制。
&__func__ —— 编译会直接报错:address of function '__func__' cannot be taken。这意味它无法被当作正常变量来取地址或传递。const char* 且实参处于字面量上下文。普通传参很容易触发编译错误或不预期行为。__func__ 在宏展开时,始终返回宏定义处的函数名,而不是调用处的函数名。这导致很多人在日志宏里误用,结果打印出来的函数名永远是宏定义所在的那个函数,完全不是预期的。std::source_location 怎么用才不掉坑它是 C++20 引入的轻量结构体,通过编译器内置支持生成调用点信息(函数名、文件、行号、列号)。关键点在于:它默认构造时捕获的是调用点,而不是定义点。但前提是——你必须显式调用 ::current() 方法。
log("msg", std::source_location::current()),别漏掉 ::current()。很多人会写成 std::source_location loc;,然后直接传 loc,结果初始化成无效值,line() == 0,function_name() 返回空字符串——白忙一场。void log(const char* msg, const std::source_location& loc = std::source_location::current()) { std::cout << loc.file_name() << ":" << loc.line() << " [" << loc.function_name() << "] " << msg << "\n";}// 调用处自动捕获当前行和函数名log("start processing"); // 输出:main.cpp:42 [main] start processing
很多人写日志宏时直接拼接 __FILE__ 和 __LINE__,却忽略了宏展开时机问题——它们总在宏体内部展开,而不是调用位置。这是个常见的翻车现场。
#define LOG(x) std::cerr << __FILE__ << ":" << __LINE__ << x —— 总显示宏定义所在行,而不是调用处。__FILE__ 和 __LINE__ 作为宏参数透传,或改用 std::source_location。如果必须兼容 C++17,可以这样修:#define LOG(x) do { ::log_impl(x, __FILE__, __LINE__, __func__); } while(0),这样宏参数会在调用处展开,获取正确信息。__FUNCTION__ 和 __PRETTY_FUNCTION__ 是 GCC/Clang 扩展,前者等价于 __func__,后者包含函数签名(如 void foo(int)),但不可移植,跨平台项目慎用。话说回来,真正麻烦的从来不是“怎么拿到名字”,而是“拿到的是哪一层的名字”——宏、内联、模板实例化、lambda 都会让 __func__ 或 source_location 指向意料之外的位置。调试时多打几行 std::source_location::current() 对比,比查文档更快定位问题。

售后无忧
立即购买>office旗舰店
售后无忧
立即购买>office旗舰店
售后无忧
立即购买>office旗舰店
售后无忧
立即购买>office旗舰店
正版软件
正版软件
正版软件
正版软件
正版软件
1
2
3
7
8