Namensräume
Varianten
Aktionen

Bedingte Einbindung

Von cppreference.com
 
 
C++ Sprache
Allgemeine Themen
Kontrollfluss
Bedingte Ausführungsaussagen
if
Iterationsanweisungen (Schleifen)
for
Bereichs-for (C++11)
Sprunganweisungen
Funktionen
Funktionsdeklaration
Lambda-Funktionsausdruck
inline-Spezifizierer
Dynamische Ausnahmespezifikationen (bis C++17*)
noexcept-Spezifizierer (C++11)
Ausnahmen
Namensräume
Typen
Spezifizierer
const/volatile
decltype (C++11)
auto (C++11)
constexpr (C++11)
consteval (C++20)
constinit (C++20)
Speicherdauer-Spezifizierer
Initialisierung
Ausdrücke
Alternative Darstellungen
Literale
Boolesch - Ganzzahl - Gleitkommazahl
Zeichen - String - nullptr (C++11)
Benutzerdefinierte (C++11)
Dienstprogramme
Attribute (C++11)
Typen
typedef-Deklaration
Typalias-Deklaration (C++11)
Umwandlungen
Speicherzuweisung
Klassen
Klassenspezifische Funktionseigenschaften
explicit (C++11)
static

Spezielle Member-Funktionen
Templates
Sonstiges
 
Präprozessor
#if#ifdef#ifndef#else#elif#elifdef#elifndef#endif
(C++23)(C++23)
(C++26)
 

Der Präprozessor unterstützt die bedingte Kompilierung von Teilen einer Quelldatei. Dieses Verhalten wird durch die Direktiven #if, #else, #elif, #ifdef, #ifndef, #elifdef, #elifndef(seit C++23) und #endif gesteuert.

Inhalt

[bearbeiten] Syntax

#if Ausdruck
#ifdef Bezeichner
#ifndef Bezeichner
#elif Ausdruck
#elifdef Bezeichner (seit C++23)
#elifndef Bezeichner (seit C++23)
#else
#endif

[bearbeiten] Erklärung

Der bedingte Präprozessorblock beginnt mit einer #if-, #ifdef- oder #ifndef-Direktive, gefolgt von optional einer beliebigen Anzahl von #elif-, #elifdef- oder #elifndef-Direktiven(seit C++23), gefolgt von optional höchstens einer #else-Direktive und wird mit einer #endif-Direktive abgeschlossen. Verschachtelte bedingte Präprozessorblöcke werden separat verarbeitet.

Jede der Direktiven #if, #ifdef, #ifndef, #elif, #elifdef, #elifndef(seit C++23) und #else steuert den Codeblock bis zur ersten #elif-, #elifdef-, #elifndef-(seit C++23), #else- oder #endif-Direktive, die nicht zu einem inneren bedingten Präprozessorblock gehört.

Die Direktiven #if, #ifdef und #ifndef prüfen die angegebene Bedingung (siehe unten) und kompilieren den gesteuerten Codeblock, wenn diese zu wahr ausgewertet wird. In diesem Fall werden nachfolgende #else-, #elifdef-, #elifndef-(seit C++23) und #elif-Direktiven ignoriert. Andernfalls, wenn die angegebene Bedingung zu falsch ausgewertet wird, wird der gesteuerte Codeblock übersprungen und die nachfolgende #else-, #elifdef-, #elifndef-(seit C++23) oder #elif-Direktive (falls vorhanden) verarbeitet. Wenn die nachfolgende Direktive #else ist, wird der von der #else-Direktive gesteuerte Codeblock bedingungslos kompiliert. Andernfalls verhält sich die #elif-, #elifdef- oder #elifndef-(seit C++23)-Direktive wie eine #if-Direktive: Sie prüft die Bedingung, kompiliert oder überspringt den gesteuerten Codeblock basierend auf dem Ergebnis und verarbeitet im letzteren Fall nachfolgende #elif-, #elifdef-, #elifndef-(seit C++23) und #else-Direktiven. Der bedingte Präprozessorblock wird mit der #endif-Direktive abgeschlossen.

[bearbeiten] Auswertung von Bedingungen

[bearbeiten] #if, #elif

Der Ausdruck kann Folgendes enthalten:

  • unäre Operatoren in Form von defined Bezeichner oder defined (Bezeichner). Das Ergebnis ist 1, wenn der Bezeichner als Makroname definiert wurde, andernfalls ist das Ergebnis 0. __has_­include und __has_cpp_attribute(seit C++20) werden in diesem Kontext so behandelt, als wären sie Namen von definierten Makros.(seit C++17)
  • (seit C++17) __has_include-Ausdrücke, die erkennen, ob eine Header- oder Quelldatei existiert.
  • (seit C++20) __has_cpp_attribute-Ausdrücke, die erkennen, ob ein gegebener Attribut-Token unterstützt wird und welche unterstützte Version.

Nach der gesamten Makroerweiterung und Auswertung von defined-, __has_include(seit C++17)- und __has_cpp_attribute(seit C++20)-Ausdrücken wird jeder Bezeichner, der kein boolesches Literal ist, durch die Zahl 0 ersetzt (dies schließt Bezeichner ein, die lexikalische Schlüsselwörter sind, aber keine alternativen Token wie and).

Anschließend wird der Ausdruck als ganzzahliger konstanter Ausdruck ausgewertet.

Wenn der Ausdruck einen Wert ungleich Null ergibt, wird der gesteuerte Codeblock einbezogen, andernfalls wird er übersprungen.

Hinweis: Bis zur Klärung des CWG-Problems 1955 unterschied sich #if Bedingung1 ... #elif Bedingung2 von #if Bedingung1 ... #else gefolgt von #if Bedingung2, da im Fall, dass Bedingung1 wahr ist, der zweite #if übersprungen wird und Bedingung2 keine wohlgeformte Bedingung sein muss, während die Bedingung2 eines #elif ein gültiger Ausdruck sein muss. Seit CWG 1955 wird auch ein #elif übersprungen, dem ein übersprungener Codeblock vorausgeht.

[bearbeiten] Kombinierte Direktiven

Prüft, ob der Bezeichner als Makroname definiert wurde.

#ifdef Bezeichner ist im Wesentlichen äquivalent zu #if defined Bezeichner.

#ifndef Bezeichner ist im Wesentlichen äquivalent zu #if !defined Bezeichner.

#elifdef Bezeichner ist im Wesentlichen äquivalent zu #elif defined Bezeichner.

#elifndef Bezeichner ist im Wesentlichen äquivalent zu #elif !defined Bezeichner.

(seit C++23)

[bearbeiten] Hinweise

Obwohl die Direktiven #elifdef und #elifndef auf C++23 abzielen, werden Implementierungen ermutigt, sie als konforme Erweiterungen in ältere Sprachmodi zurückzuportieren.

[bearbeiten] Beispiel

#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
}

Mögliche Ausgabe

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

[bearbeiten] Fehlerberichte

Die folgenden Verhaltensändernden Fehlerberichte wurden rückwirkend auf zuvor veröffentlichte C++-Standards angewendet.

DR angewendet auf Verhalten wie veröffentlicht Korrigiertes Verhalten
CWG 1955 C++98 Der Ausdruck von #elif war erforderlich, gültig zu sein. #elif wird übersprungen.

[bearbeiten] Siehe auch

C-Dokumentation für Bedingte Einbindung