名前空間
変種
操作

条件付きインクルード

From cppreference.com
 
 
C++言語
全般
フロー制御
条件実行文
if
繰り返し文 (ループ)
for
範囲for (C++11)
ジャンプ文
関数
関数宣言
ラムダ式
inline指定子
動的例外仕様 (C++17まで*)
noexcept指定子 (C++11)
例外
名前空間
指定子
const/volatile
decltype (C++11)
auto (C++11)
constexpr (C++11)
consteval (C++20)
constinit (C++20)
記憶域期間指定子
初期化
代替表現
リテラル
ブーリアン - 整数 - 浮動小数点数
文字 - 文字列 - nullptr (C++11)
ユーザー定義 (C++11)
ユーティリティ
属性 (C++11)
typedef宣言
型エイリアス宣言 (C++11)
キャスト
メモリ確保
クラス
クラス固有の関数プロパティ
explicit (C++11)
static

特殊メンバ関数
テンプレート
その他
 
プリプロセッサ
#if#ifdef#ifndef#else#elif#elifdef#elifndef#endif
(C++23)(C++23)
(C++26)
 

プリプロセッサは、ソースファイルの一部を条件付きでコンパイルする機能をサポートしています。この動作は、#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ディレクティブは、指定された条件(下記参照)をテストし、それがtrueと評価された場合、制御されたコードブロックをコンパイルします。その場合、その後の#else#elifdef#elifndef(C++23以降)および#elifディレクティブは無視されます。そうでなければ、指定された条件がfalseと評価された場合、制御されたコードブロックはスキップされ、その後の#else#elifdef#elifndef(C++23以降)または#elifディレクティブ(存在する場合)が処理されます。もしその後のディレクティブが#elseであれば、#elseディレクティブによって制御されるコードブロックは無条件にコンパイルされます。そうでなければ、#elif#elifdef、または#elifndef(C++23以降)ディレクティブは#ifディレクティブであるかのように動作します:条件をチェックし、結果に基づいて制御されたコードブロックをコンパイルまたはスキップし、後者の場合はその後の#elif#elifdef#elifndef(C++23以降)および#elseディレクティブを処理します。条件付きプリプロセッシングブロックは#endifディレクティブで終了します。

[編集] 条件の評価

[編集] #if, #elif

には、以下を含めることができます。

  • 単項演算子としてdefined 識別子またはdefined (識別子)。結果は、識別子マクロ名として定義されている場合は1、そうでなければ0です。__has_­include __has_cpp_attribute(C++20以降)は、この文脈において定義済みマクロ名であるかのように扱われます。(C++17以降)
  • (C++17以降) ヘッダまたはソースファイルが存在するかどうかを検出する__has_include式。
  • (C++20以降) 特定の属性トークンがサポートされているかどうか、およびそのサポートされているバージョンを検出する__has_cpp_attribute式。

すべてのマクロ展開とdefined__has_include(C++17以降)、および__has_cpp_attribute(C++20以降)式の評価の後、ブールリテラルではないすべての識別子は数値0に置き換えられます(これには、字句的にはキーワードであるが、andのような代替トークンではない識別子が含まれます)。

その後、式は整数定数式として評価されます。

が非ゼロ値に評価された場合、制御されたコードブロックは含まれ、そうでなければスキップされます。

注:CWG issue 1955の解決まで、#if cond1 ... #elif cond2#if cond1 ... #elseの後に#if cond2が続くものとは異なります。なぜなら、cond1がtrueの場合、2番目の#ifはスキップされ、cond2は整形式である必要はありませんが、#elifcond2は有効な式でなければならないからです。CWG 1955以降、スキップされたコードブロックを導く#elifもスキップされます。

[編集] 結合されたディレクティブ

識別子がマクロ名として定義されているかどうかをチェックします。

#ifdef 識別子は、本質的に#if defined 識別子と等価です。

#ifndef 識別子は、本質的に#if !defined 識別子と等価です。

#elifdef 識別子は、本質的に#elif defined 識別子と等価です。

#elifndef 識別子は、本質的に#elif !defined 識別子と等価です。

(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
 
 
// Note that if a compiler does not support C++23's #elifdef/#elifndef
// directives then the "unexpected" block (see below) will be selected.
#ifdef CPU
    std::cout << "4: no1\n";
#elifdef GPU
    std::cout << "4: no2\n";
#elifndef RAM
    std::cout << "4: yes\n"; // expected block
#else
    std::cout << "4: no!\n"; // unexpectedly selects this block by skipping
                             // unknown directives and "jumping" directly
                             // from "#ifdef CPU" to this "#else" block
#endif
 
// To fix the problem above we may conditionally define the
// macro ELIFDEF_SUPPORTED only if the C++23 directives
// #elifdef/#elifndef are 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"; // expected block
    #else
        std::cout << "4: no3\n";
    #endif
#else // when #elifdef unsupported use old verbose `#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"; // expected block
    #else
        std::cout << "4: no3\n";
    #endif
#endif
}

実行結果の例

1: yes
2: yes
3: yes
4: no!
4: yes

[編集] 欠陥報告

以下の動作変更を伴う欠陥報告が、以前に公開されたC++標準に遡って適用されました。

DR 適用対象 公開された動作 正しい動作
CWG 1955 C++98 失敗した#elifの式は有効である必要がありました 失敗した#elifはスキップされます

[編集] 関連項目

条件付きインクルードCドキュメント
English 日本語 中文(简体) 中文(繁體)