文本替换宏: 语法 对于版本 (3,4),替换列表 可以含有记号序列“__VA_OPT__ ( 内容 )”,若 __VA_ARGS__ 非空,则它会被 内容 替换,否则不展开成任何内容。
#define 标识符 替换列表(可选) | (1) | #define 标识符( 形参 ) 替换列表(可选) | (2) | #define 标识符( 形参, ... ) 替换列表(可选) | (3) | #define 标识符( ... ) 替换列表(可选) | (4) | #undef 标识符 | (5) | # 与 ## 运算符 | 仿函数宏中,替换列表 中放在标识符前的 # 运算符,使标识符运行形参替换,并将其结果以引号包围,实际上创建一个字符串字面量。另外,预处理器为内嵌的字符串字面量(若它存在)外围的引号添加反斜杠以进行转义,并按需要双写字符串中的反斜杠。 | #undef 指令 | #undef 指令取消定义 标识符,即取消 #define 指令所作的 标识符 定义。若标识符未关联到宏,则忽略该指令 | __cplusplus | 代表所用的 C++ 标准版本,展开成值 199711L(C++11 前) 、 201103L(C++11) 、 201402L(C++14) 、 201703L(C++17) 或 202002L(C++20) (宏常量) | __STDC_HOSTED__ | 若实现有宿主(运行在 OS 下)则展开成整数常量 1,若实现自立(不随 OS 运行)则展开成 0 | _FILE__ | 展开成当前文件名,作为字符串字面量,可用 #line 指令更改 | __LINE__ | 展开成源文件行号,整数常量,可用 #line 指令更改 | __DATE__ | 展开成翻译日期,形式为 "Mmm dd yyyy" 的字符串。若月中日期数小于 10 则 "dd" 的首字符为空格。月份名如同以 std::asctime() 生成 | __TIME__ | 展开成翻译时间,形式为 "hh:mm:ss" 的字符串字面量 | __STDCPP_DEFAULT_NEW_ALIGNMENT__ | 展开成 std::size_t 字面量,其值为对不具对齐的 operator new 的调用所保证的对齐 | __STDC__ | 若存在则为实现定义值,典型地用于指示 C 遵从性 | __STDC_VERSION__ | 若存在则为实现定义值 | __STDC_ISO_10646__ | 若 wchar_t 使用 Unicode ,则展开成 yyyymmL 形式的整数常量,日期指示所支持的 Unicode 的最近版本 | __STDC_MB_MIGHT_NEQ_WC__ | 若对于基本字符集成员 'x' == L'x' 可能为假,则展开成 1,如在基于 EBCDIC 并且为 wchar_t 使用 Unicode 的系统上。 | __STDCPP_STRICT_POINTER_SAFETY__ | 若实现支持严格 std::pointer_safety 则展开成 1 | __STDCPP_THREADS__ | 若程序能拥有多于一个执行线程则展开成 1 | __func__ | 注意:在每个函数体的作用域内部,都有一个名为 __func__ 的特殊的函数局域预定义变量,定义为一个持有具有实现定义格式的函数名的静态字符数组。它不是预处理器宏,但它与 __FILE__ 和 __LINE__ 一起使用 |
| #define F(...) f(0 __VA_OPT__(,) __VA_ARGS__) #define G(X, ...) f(0, X __VA_OPT__(,) __VA_ARGS__) #define SDEF(sname, ...) S sname __VA_OPT__(= { __VA_ARGS__ }) F(a, b, c) // 替换为 f(0, a, b, c) F() // 替换为 f(0) G(a, b, c) // 替换为 f(0, a, b, c) G(a, ) // 替换为 f(0, a) G(a) // 替换为 f(0, a) SDEF(foo); // 替换为 S foo; SDEF(bar, 1, 2); // 替换为 S bar = { 1, 2 };
# 出现于 __VA_ARGS__ 之前时,展开后的 __VA_ARGS__ 整体被包在引号中: |
#define showlist(...) puts(#__VA_ARGS__) showlist(); // 展开成 puts("") showlist(1, "x", int); // 展开成 puts("1, \"x\", int" // 制造函数工厂并使用它 #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' 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); // 注意缺少引号 } |
源文件包含: 语法
#include <文件名> | (1) | | #include "文件名" | (2) | | __has_include ( " 文件名 " ) __has_include ( < 文件名 > ) | (3) | (C++17 起) |
容许任何预处理记号(宏常量或表达式)作为给 #include 和 __has_include (C++17 起)的实参,只要它们展开成 < > 或 " " 所环绕的字符序列即可。 解释 1) 以由实现定义的方式搜索文件。此语法的意图是搜索由实现所掌控的文件。典型实现仅搜索标准包含目录。这些标准包含目录中隐含地包含标准 C++ 库和标准 C 库。用户通常能通过编译器选项来控制标准包含目录。 2) 以由实现定义的方式搜索文件。此语法的意图是搜索不被实现所掌控的文件。典型实现首先于当前文件所在的目录搜索,然后仅当找不到文件时,才在 (1) 中的标准包含目录搜索。 3) 预处理器常量表达式,若找到文件名则求值为 1,而若找不到则求值为 0。若其实参不是给 #include 指令的有效实参,则程序非良构。 #pragma once:若已包含了同一文件(这里以特定于 OS 的方式确定文件的身份),则禁止处理该文件。 | #if __has_include(<optional>) # include <optional> # define have_optional 1 namespace guard { using std::optional; } #elif __has_include(<experimental/optional>) # include <experimental/optional> # define have_optional 1 # define experimental_optional 1 namespace guard { using std::experimental::optional; } #else # define have_optional 0 #endif int main() { if (have_optional) std::cout << "<optional> is present.\n"; int x = 42; #if have_optional == 1 guard::optional<int> i = x; #else int* i = &x; #endif std::cout << "i = " << *i << '\n'; } |
实现定义的行为控制 实现定义的行为受 #pragma 指令控制。 语法
#pragma 语用形参 | (1) | | _Pragma ( 字符串字面量 ) | (2) | (C++11 起) |
1) 具有由实现定义的行为方式 2) 从 字符串字面量 移除 L 前缀(若存在),外层引号,及前导/尾随空白符,将每个 \" 替换为 "、将每个 \\ 替换为 \,然后将结果记号化(如翻译阶段 3 中一样),再如同将结果作为 (1) 中的输入来予以使用。 #pragma once #pragma once 是受到绝大多数现代编译器支持的非标准语用。当某个头文件中包含它时,指示编译器只对其分析一次,即使它在同一源文件中(直接或间接)被包含了多次也是如此。 #pragma pack 此 pragma 族控制后继定义的结构体、联合体、类的最大对齐。
#pragma pack(实参) | (1) | #pragma pack() | (2) | #pragma pack(push) | (3) | #pragma pack(push, 实参) | (4) | #pragma pack(pop) | (5) |
其中 arg 实参是小的 2 的幂,指定以字节计的新对齐。 1) 设置当前对齐为值 实参 。 2) 设置当前对齐为默认值(由命令行选项指定)。 3) 推入当前对齐的值到内部栈。 4) 推入当前对齐的值到内部栈然后设置当前对齐为值 实参 。 5) 从内部栈弹出顶条目然后设置(恢复)当前对齐为该值。 #pragma pack 可以指定结构的对齐,然而它不能使结构过对齐。 | |