Conditional inclusion
プリプロセッサはソースファイルの一部を条件付きでコンパイルする機能をサポートしています。この動作は
#if
、
#else
、
#elif
、
#ifdef
、
#ifndef
、
#elifdef
、
#elifndef
(C++23以降)
、および
#endif
ディレクティブによって制御されます。
目次 |
構文
#if
式
|
|||||||||
#ifdef
識別子
|
|||||||||
#ifndef
識別子
|
|||||||||
#elif
式
|
|||||||||
#elifdef
識別子
|
(C++23以降) | ||||||||
#elifndef
識別子
|
(C++23以降) | ||||||||
#else
|
|||||||||
#endif
|
|||||||||
説明
条件付きプリプロセスブロックは
#if
、
#ifdef
または
#ifndef
ディレクティブで始まり、任意の数の
#elif
、
#elifdef
または
#elifndef
(C++23以降)
ディレクティブを任意の数だけ含み、オプションで最大1つの
#else
ディレクティブを含み、
#endif
ディレクティブで終了します。内部の条件付きプリプロセスブロックは個別に処理されます。
#if
、
#ifdef
、
#ifndef
、
#elif
、
#elifdef
、
#elifndef
(C++23以降)
および
#else
ディレクティブは、最初の
#elif
、
#elifdef
、
#elifndef
(C++23以降)
、
#else
、
#endif
ディレクティブに到達するまでのコードブロックを制御します。ただし、内側の条件付きプリプロセッシングブロックに属さないものに限ります。
#if
、
#ifdef
および
#ifndef
ディレクティブは指定された条件(後述)を評価し、真と評価された場合、制御されるコードブロックをコンパイルします。その場合、後続の
#else
、
#elifdef
、
#elifndef
、
(C++23以降)
および
#elif
ディレクティブは無視されます。それ以外の場合、指定された条件が偽と評価されると、制御されるコードブロックはスキップされ、後続の
#else
、
#elifdef
、
#elifndef
、
(C++23以降)
または
#elif
ディレクティブ(存在する場合)が処理されます。後続のディレクティブが
#else
の場合、
#else
ディレクティブによって制御されるコードブロックは無条件でコンパイルされます。それ以外の場合、
#elif
、
#elifdef
または
#elifndef
(C++23以降)
ディレクティブは
#if
ディレクティブと同様に動作します:条件をチェックし、結果に基づいて制御されるコードブロックをコンパイルまたはスキップし、後者の場合、後続の
#elif
、
#elifdef
、
#elifndef
、
(C++23以降)
および
#else
ディレクティブを処理します。条件付きプリプロセッシングブロックは
#endif
ディレクティブによって終了します。
条件評価
#if, #elif
expression
は
defined
identifier
または
defined (
identifier
)
の形式の単項演算子を含むことができます。結果は、
identifier
が
マクロ名として定義されている
場合は
1
であり、そうでない場合は結果は
0
です。
|
expression は以下の式も含むことができます:
上記で言及された識別子は、この文脈では定義済みマクロの名前であるかのように扱われます。 |
(C++17以降) |
すべてのマクロ展開と
defined
および前述の式の評価が完了した後、
boolean literal
ではない識別子はすべて数値
0
に置き換えられます
(これには、字句的にはキーワードである識別子も含まれますが、
and
のような代替トークンは含まれません)。
その後、式は integral constant expression として評価されます。
もし expression が非ゼロ値に評価される場合、制御されるコードブロックが含まれ、それ以外の場合はスキップされます。
注記:
CWG issue 1955
が解決されるまで、
#if
cond1
...
#elif
cond2
は、
#if
cond1
...
#else
に続く
#if
cond2
とは異なります。なぜなら、
cond1
が真の場合、2番目の
#if
はスキップされ、
cond2
が正しい形式である必要はありませんが、
#elif
の
cond2
は有効な式でなければならないからです。CWG 1955以降では、スキップされるコードブロックを導く
#elif
もスキップされます。
結合ディレクティブ
識別子が マクロ名として定義されているか をチェックします。
#ifdef
identifier
は本質的に
#if defined
identifier
と同等です。
#ifndef
identifier
は本質的に
#if !defined
identifier
と同等です。
|
|
(C++23以降) |
注記
#elifdef
および
#elifndef
ディレクティブはC++23を対象としていますが、実装側はこれらを準拠した拡張機能として古い言語モードにバックポートすることが推奨されます。
例
#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"; // 未知のディレクティブをスキップし、直接この"#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 }
出力例:
1: yes 2: yes 3: yes 4: no! 4: yes
不具合報告
以下の動作変更の欠陥報告書は、以前に公開されたC++規格に対して遡及的に適用されました。
| DR | 適用対象 | 公開時の動作 | 正しい動作 |
|---|---|---|---|
| CWG 1955 | C++98 | failed #elif の式が有効であることが要求されていた | failed #elif はスキップされる |
関連項目
|
C documentation
for
Conditional inclusion
|