预处理器¶
`module` 和 `import` 指令也是预处理指令。(C++20 起)
预处理器有一下几类
- 有条件编译源文件的某些部分(由 #if、#ifdef、#ifndef、#else、#elif、#elifdef、#elifndef (C++23 起) 和 #endif
指令控制)。
- 替换 文本宏,同时可能对标识符进行拼接或加引号(由 #define
和 #undef
指令与 # 和 ##
运算符控制)。
- 包含 其他文件(由 #include
指令控制并以 __has_include
检查 (C++17 起))。
- 导致错误或警告(C++23 起)(由指令 #error
或 #warning
(C++23 起) 控制)。
- 由实现定义的行为(由 #pragma
指令和 _Pragma
运算符 (C++11 起) 控制)。
- 对预处理器可用的文件名和行信息(由 #line
指令控制)
1 条件包含¶
- `#if、#elif、#else`是一组。
- `#ifdef、#ifndef、#elifdef、#elifndef、#endif`是一组。
- 语法如下:
语法 | 注释 |
---|---|
#if 表达式 |
#if 0 这个条件是假 |
#elif 表达式 |
|
#else |
|
#ifdef 标识符 |
等价于 #if defined |
#ifndef 标识符 |
等价于 #if !defined |
#elifdef 标识符 |
(C++23 起) 等价于#elif defined |
#elifndef 标识符 |
(C++23 起) 等价于 #elif !defined |
#endif |
- 示例:
#define ABCD 2
#include <iostream>
int main()
{
#ifdef ABCD
std::cout << "1: yes\n";
#else
std::cout << "1: no\n";
#endif
#ifndef ABCD
std::cout << "2: no1\n";
#elif ABCD == 2
std::cout << "2: yes\n";
#else
std::cout << "2: no2\n";
#endif
#if !defined(DCBA) && (ABCD < 2*4-3)
std::cout << "3: yes\n";
#endif
// 注意若编译器不支持 C++23 的 #elifdef/#elifndef 指令则会选择“不期待”块(见后述)。
#ifdef CPU
std::cout << "4: no1\n";
#elifdef GPU
std::cout << "4: no2\n";
#elifndef RAM
std::cout << "4: yes\n"; // 期待的块
#else
std::cout << "4: no!\n"; // 由于跳过未知的指令不期待地选择此块
// 并直接从 "#ifdef CPU" “跳”到此 "#else" 块
#endif
// 为修复此问题,我们可以条件性地仅若支持 C++23 指令 #elifdef/#elifndef
// 才定义 ELIFDEF_SUPPORTED 宏。
#if 0
#elifndef UNDEFINED_MACRO
#define ELIFDEF_SUPPORTED
#else
#endif
#ifdef ELIFDEF_SUPPORTED
#ifdef CPU
std::cout << "4: no1\n";
#elifdef GPU
std::cout << "4: no2\n";
#elifndef RAM
std::cout << "4: yes\n"; // 期待的块
#else
std::cout << "4: no3\n";
#endif
#else // 不支持 #elifdef 时使用累赘的旧 `#elif defined`
#ifdef CPU
std::cout << "4: no1\n";
#elif defined GPU
std::cout << "4: no2\n";
#elif !defined RAM
std::cout << "4: yes\n"; // 期待的块
#else
std::cout << "4: no3\n";
#endif
#endif
}
2 文本替换宏¶
- 语法:
| 语法 | 注释 |
| ------------------------------------ | ---------- |
|
#define
标识符 替换列表(可选) | | |#define
标识符(形参) 替换列表(可选) | | |#define
标识符(形参, ... )替换列表(可选) | (C++11 起) | |#define
标识符( ... )替换列表(可选) | (C++11 起) | |#undef
标识符* | |
3 源文件包含¶
`#include`只是把其它文件*完整的拷贝到*当前文件中。(在#include行的位置替换)
- 功能:将其他源文件包含到当前源文件中紧随指令之后的一行。
-
语法: | 语法 | 注释 | | ------------------------------------------------------------ | ---------- | |
#include < h字符序列 >
换行 | | |#include " q字符序列 "
换行 | | |#include
记号序列 换行 | | |__has_include ( " q字符序列 " )
和__has_include ( < h字符序列 > )
| (C++17 起) 检查一个头或源文件是否可以被包含。| |__has_include ( 字符串字面量 )
和__has_include ( < h记号序列 > )
| (C++17 起) | -
示例
#if __has_include(<optional>)
# include <optional>
# define has_optional 1
template<class T> using optional_t = std::optional<T>;
#elif __has_include(<experimental/optional>)
# include <experimental/optional>
# define has_optional -1
template<class T> using optional_t = std::experimental::optional<T>;
#else
# define has_optional 0
# include <utility>
template<class V> class optional_t {
V v_{}; bool has_{false};
public:
optional_t() = default;
optional_t(V&& v) : v_(v), has_{true} {}
V value_or(V&& alt) const& { return has_ ? v_ : alt; }
/*...*/
};
#endif
#include <iostream>
int main()
{
if (has_optional > 0)
std::cout << "<optional> 存在\n";
else if (has_optional < 0)
std::cout << "<experimental/optional> 存在\n";
else
std::cout << "<optional> 不存在\n";
optional_t<int> op;
std::cout << "op = " << op.value_or(-1) << '\n';
op = 42;
std::cout << "op = " << op.value_or(-1) << '\n';
}
- 可能的输出
<optional> 存在
op = -1
op = 42