编译期获取当前行号用__line__宏,它在编译时展开为当前源文件的行号(整型常量),无需运行时开销,例如在第42行写std::cout。

编译期获取当前行号用 __LINE__
直接用预定义宏 __LINE__,它在编译时展开为当前源文件的行号(整型常量),无需运行时开销。比如你在第 42 行写 std::cout ,输出就是 <code>42。
注意:它反映的是宏展开点的行号,不是调用点——如果封装成函数再调用,__LINE__ 会变成函数内部那行的编号,不是调用者的行号。
- 适用于日志、断言、调试信息中快速标记位置
- 不能用于 constexpr 函数参数传递(除非在调用点直接展开)
- 不同编译器都支持,C++ 标准保证其行为
运行时想记录调用者行号?必须靠宏包装
想让一个日志函数自动打出“调用这行的行号”,不能写个普通函数,得用宏把 __LINE__ 和 __FILE__ 捕获下来传进去。
例如:
#define LOG(msg) do { \
std::cerr
- 直接写
LOG("hello");就能打出当前行号,而不是宏定义所在的行号 - 不用
do-while(0)包裹的话,遇到if (...) LOG("x"); else ...会语法错误 - 如果要用
std::format或其他模板函数,宏里传参要小心类型退化(比如字符串字面量是const char*)
__LINE__ 和 std::source_location(C++20)怎么选?
C++20 引入了 std::source_location,可以运行时获取文件名、函数名、行号等,但它不是零开销:构造时可能触发少量运行时计算(尽管多数实现做了优化)。
对比:
-
__LINE__:纯编译期,无任何运行时成本,但只能拿到行号 -
std::source_location::current():可拿到完整上下文(file_name()、function_name()、line()),但每次调用都生成一个对象;默认构造不等于当前调用点,必须显式写std::source_location::current() - 若只想要行号且追求极致轻量,
__LINE__更直接;若需要统一抽象、未来可能扩展字段,或已用 C++20 且不在热路径,std::source_location更安全、不易出错
常见坑:别在头文件里无条件展开 __LINE__
如果在头文件里定义了一个内联函数,里面用了 __LINE__,那么每个包含它的翻译单元里,该函数里的 __LINE__ 都会变成“在那个 .cpp 文件里被实例化的那一行”的行号,而不是头文件里写的那一行。
- 后果:日志行号跳变、断言失败位置混乱
- 解决办法:要么改用宏(确保展开点即调用点),要么用
std::source_location并让调用者传入std::source_location::current() - 尤其注意模板函数里混用
__LINE__—— 它绑定的是实例化点,不是定义点
source_location 的取舍,本质是编译期确定性 vs 运行时灵活性的权衡,而最容易被忽略的,是头文件中内联函数 + __LINE__ 组合带来的非预期偏移。C++免费学习笔记(深入):立即使用
在学习笔记中,你将探索 C++ 的入门与实战技巧!











