Namespaces
Variants

Conditional inclusion

From cppreference.net
C++ language
General topics
Flow control
Conditional execution statements
Iteration statements (loops)
Jump statements
Functions
Function declaration
Lambda function expression
inline specifier
Dynamic exception specifications ( until C++17* )
noexcept specifier (C++11)
Exceptions
Namespaces
Types
Specifiers
constexpr (C++11)
consteval (C++20)
constinit (C++20)
Storage duration specifiers
Initialization
Expressions
Alternative representations
Literals
Boolean - Integer - Floating-point
Character - String - nullptr (C++11)
User-defined (C++11)
Utilities
Attributes (C++11)
Types
typedef declaration
Type alias declaration (C++11)
Casts
Memory allocation
Classes
Class-specific function properties
Special member functions
Templates
Miscellaneous
Preprocessor
#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 ディレクティブは指定された条件(後述)を評価し、真と評価された場合、制御されるコードブロックをコンパイルします。その場合、後続の #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 は以下の式も含むことができます:

  • __has_include 式 - ヘッダーまたはソースファイルが存在するかどうかを検出します。
  • __has_cpp_attribute 式 - 指定された属性トークンがサポートされているかどうか、およびそのサポートバージョンを検出します。
(C++20以降)
  • __has_embed 式 - 埋め込み可能なリソースが利用可能かどうかを検出します。
(C++26以降)

上記で言及された識別子は、この文脈では定義済みマクロの名前であるかのように扱われます。

(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 と同等です。

#elifdef identifier は本質的に #elif defined identifier と同等です。

#elifndef identifier は本質的に #elif !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