10 预处理器和宏
约 898 字大约 3 分钟
2025-06-22
C++17标准为预处理器增加了一个新特性__has_include,用于判断某个头文件是否能够被包 含进来
#if __has_include(<optional>)
# include <optional>
# define have_optional 1
#elif __has_include(<experimental/optional>)
# include <experimental/optional>
# define have_optional 1
# define experimental_optional 1
#else
# define have_optional 0
#endif
如果__has_include()中的头文件optional可以被包含进来,那么表达式求值为1;否 则求值为0。 __ has_include的实参必须和#include的实参具有同样的形式,否则会导致编译错误。另外,__ has_include并不关心头文件是否已经被包含进来。
特性测试宏
C++20标准添加了一组用于测试功能特性的宏,这组宏可以帮助我们测试当前的编译环境对 各种功能特性的支持程度 这些宏展开的值会随着特性的变更而更新
属性特性测试宏(__ has_cpp_attribute)
可以指示编译环境是否支持某种属性,该属性可以是标准属性,也可以是编译环境厂商特有的属性。标准属性将被展开为该属性添加到标准时的年份和月份,而厂商特有的属性将被展开为一个非零的值:
cout << __has_cpp_attribute(deprecated); // 输出结果如下:201309
输出201309,代表该属性在2013年9月加入标准,并且被当前编译环境支持。
当前的标准属性表:
语言功能特性测试宏
以下列表的宏代表编译环境所支持的语言功能特性,每个宏将被展开为该特性添加到标准时 的年份和月份。
标准库功能特性测试宏
以下列表的宏代表编译环境所支持的标准库功能特性,它们通常包含在头文件或者 表中的任意对应头文件中。同样,每个宏将被展开为该特性添加到标准时的年份和月份。
新增宏VA_OPT
从C99标准开始,C语言引入了可变参数宏__VA_ARGS__,而顺理成章的C++11标准也将其纳 入标准当中。__ VA_ARGS__常见的用法集中于打印日志上
#define LOG(msg,...) printf("[" __FILE__ ":%d] " msg, __LINE__, __VA_ARGS__)
int main(){
LOG("Hello %d", 2024);
}
LOG的使用和printf非常相似,并且可以很方便地将代码文件和行数记录到日志当中。不过它 们也并非完全相同 对于函数printf来说,除了第一个参数以外的其他参数都是可选的
printf("Hello 2020"); // 编译成功
LOG("Hello 2020");//编译失败
//展开是printf("[" __FILE__ ":%d] " "Hello 2020", __LINE__, );
函数的最后多出了一个逗号 我们可以使用##连接逗号和__ VA_ARGS__
#define LOG(msg, ...) printf("[" __FILE__ ":%d] " msg, __LINE__, ##__VA_ARGS__)
int main(){
LOG("Hello 2020"); // 编译成功
}
为了用更加标准的方法解决以上问题,C++20标准引入了一个新的宏__VA_OPT__令可变参数宏 更易于在可变参数为空的情况下使用。将代码修改为:
#define LOG(msg, …) printf("[" __FILE__ ":%d] " msg, __LINE__ __VA_OPT__ (,) __VA_ARGS__)
__ LINE__后面的逗号被修改为__VA_OPT__(,),这是告诉编译器这个 逗号是可选的。当可变参数不为空的时候逗号才会被替换出来,否则就会忽略这个逗号。 对于下面两句日志的替换结果
LOG("Hello 2020");//没有可变参数
//printf("[" __FILE__ ":%d] " "Hello 2020", __LINE__);
LOG("Hello %d", 2020);//存在可变参数
//printf("[" __FILE__ ":%d] " "Hello %d", __LINE__, 2020);
贡献者
版权所有
版权归属:PinkDopeyBug