Replacing text macros
预处理器支持文本宏替换。同时也支持函数式文本宏替换。
目录语法
说明
#define 指令#define 指令将 identifier 定义为宏,即指示编译器将后续出现的绝大多数 identifier 替换为 replacement-list ,该替换列表还会被进一步处理。例外情况遵循 扫描与替换规则 。若该标识符已被定义为任何类型的宏,则程序非良构,除非宏定义完全相同。 对象式宏对象式宏会将所有出现的已定义 标识符 替换为 替换列表 。版本(1)的 #define 指令的行为正是如此。 函数式宏类函数宏会将每个出现的已定义 标识符 替换为 替换列表 ,同时接收若干参数,这些参数随后会替换 替换列表 中对应的 形参 出现位置。 类函数宏调用的语法与函数调用语法相似:每个宏名称实例后紧跟 ( 作为下一个预处理标记时,会引入被 替换序列 所取代的标记序列。该序列由匹配的 ) 标记终止,处理过程中会跳过中间成对出现的左右括号。
对于版本(2),实参数量必须与宏定义中的形参数量相同。对于版本(3,4),实参数量不得少于形参数量(
不
(since C++20)
计算
版本(2)的 #define 指令定义了一个简单的函数式宏。
版本 (3) 的
#define
指令定义了一个可变参数函数式宏。额外的参数(称为
可变参数
)可通过
版本 (4) 的
#define
指令定义了一个类似函数的宏,该宏具有可变数量的参数,但没有常规参数。这些参数(称为
可变参数
)只能通过
注意:如果函数式宏的实参包含未被匹配的左右括号保护的逗号(最常见于模板实参列表,如 assert ( std:: is_same_v < int , int > ) ; 或 BOOST_FOREACH ( std:: pair < int , int > p, m ) ),该逗号会被解释为宏参数分隔符,导致因参数数量不匹配而引发编译失败。 扫描与替换
注意,可以定义伪递归宏:
运行此代码
#define EMPTY #define SCAN(x) x #define EXAMPLE_() EXAMPLE #define EXAMPLE(n) EXAMPLE_ EMPTY()(n-1) (n) EXAMPLE(5) SCAN(EXAMPLE(5)) 输出: EXAMPLE_ ()(5 -1) (5) EXAMPLE_ ()(5 -1 -1) (5 -1) (5) 保留宏名称一个 包含标准库头文件 的翻译单元不得对任何 标准库头文件 中声明的名称进行 #define 或 #undef 操作。 使用标准库任何部分的翻译单元不允许 #define 或 #undef 在词法上与下列名称相同的标识符:
否则,行为是未定义的。
# 与 ## 运算符在函数式宏中, # 运算符置于 替换列表 中的标识符前时,会对该标识符进行参数替换并将结果用引号括起,实际上创建了一个字符串字面量。此外,预处理器会添加反斜杠来转义嵌入字符串字面量(如果存在)周围的引号,并根据需要将字符串内的反斜杠加倍。所有首尾空白符会被移除,文本中间(但不包括嵌入字符串字面量内部)的连续空白符序列会被压缩为单个空格。此操作称为“字符串化”。若字符串化的结果不是有效的字符串字面量,则行为未定义。
在
替换列表
中,任意两个相邻标识符之间的
##
运算符会对这两个标识符(不会先进行宏展开)执行参数替换,然后将结果连接起来。此操作称为"连接"或"标记粘贴"。只有能共同构成有效标记的符号才能被粘贴:组成更长标识符的标识符、组成数字的数码,或组成
注意:某些编译器提供了一个扩展,允许在逗号后和
#undef 指令#undef 指令用于取消定义 标识符 ,即撤销之前通过 #define 指令对 标识符 所作的定义。若该标识符未关联任何宏,则该指令将被忽略。 预定义宏以下宏名称在每个翻译单元中都是预定义的:
以下附加宏名称可能由实现预定义:
这些宏的值(除了
示例
运行此代码
#include <iostream> // 创建函数工厂并使用 #define FUNCTION(name, a) int fun_##name() { return a; } FUNCTION(abcd, 12) FUNCTION(fff, 2) FUNCTION(qqq, 23) #undef FUNCTION #define FUNCTION 34 #define OUTPUT(a) std::cout << "output: " #a << '\n' // 在后续宏定义中使用宏 #define WORD "Hello " #define OUTER(...) WORD #__VA_ARGS__ int main() { std::cout << "abcd: " << fun_abcd() << '\n'; std::cout << "fff: " << fun_fff() << '\n'; std::cout << "qqq: " << fun_qqq() << '\n'; std::cout << FUNCTION << '\n'; OUTPUT(million); //注意没有引号 std::cout << OUTER(World) << '\n'; std::cout << OUTER(WORD World) << '\n'; } 输出: abcd: 12 fff: 2 qqq: 23 34 output: million Hello World Hello WORD World 缺陷报告下列行为变更缺陷报告被追溯应用于先前发布的 C++ 标准。
另请参阅
| |||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||