Namespaces
Variants

if statement

From cppreference.net
C++ language
General topics
Flow control
Conditional execution statements
if
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

Führt bedingt eine weitere Anweisung aus.

Wird verwendet, wenn Code basierend auf einer Bedingung ausgeführt werden muss , oder ob die if -Anweisung in einem offensichtlich konstant ausgewerteten Kontext evaluiert wird (since C++23) .

Inhaltsverzeichnis

Syntax

attr  (optional) if constexpr (optional)
( init-statement  (optional) condition ) statement-true
(1)
attr  (optional) if constexpr (optional)
( init-statement  (optional) condition ) statement-true else statement-false
(2)
attr  (optional) if ! (optional) consteval compound-statement (3) (seit C++23)
attr  (optional) if ! (optional) consteval compound-statement else statement (4) (seit C++23)
1) if Anweisung ohne einen else Zweig
2) if Anweisung mit einem else Zweig
3) consteval if-Anweisung ohne einen else Zweig
4) consteval if-Anweisung mit einem else Zweig
attr - (seit C++11) beliebig viele Attribute
constexpr - (seit C++17) falls vorhanden, wird die Anweisung zu einer constexpr-if-Anweisung
init-statement - (seit C++17) entweder
(seit C++23)

Beachten Sie, dass jede init-statement mit einem Semikolon enden muss. Deshalb wird sie informell oft als Ausdruck oder Deklaration gefolgt von einem Semikolon beschrieben.

condition - eine Bedingung
statement-true - die Anweisung , die ausgeführt wird, falls condition zu true ausgewertet wird
statement-false - die Anweisung, die ausgeführt wird, falls condition zu false ausgewertet wird
compound-statement - die Verbundanweisung , die ausgeführt wird, wenn die if -Anweisung in einem offensichtlich konstant ausgewerteten Kontext ausgewertet wird (oder nicht in einem solchen Kontext ausgewertet wird, falls ! vor consteval steht)
statement - die Anweisung (muss eine Verbundanweisung sein, siehe unten ), die ausgeführt wird, wenn die if -Anweisung nicht in einem offensichtlich konstant ausgewerteten Kontext ausgewertet wird (oder in einem solchen Kontext ausgewertet wird, falls ! vor consteval steht)

Bedingung

Eine Bedingung kann entweder ein Ausdruck oder eine einfache Deklaration sein.

  • Wenn es syntaktisch als eine structured binding Deklaration aufgelöst werden kann, wird es als eine structured binding Deklaration interpretiert.
(since C++26)
  • Wenn es syntaktisch als Ausdruck aufgelöst werden kann, wird es als Ausdruck behandelt. Andernfalls wird es als Deklaration behandelt die keine strukturierte Bindungsdeklaration ist (seit C++26) .

Wenn die Steuerung die Bedingung erreicht, ergibt die Bedingung einen Wert, der verwendet wird, um zu bestimmen, welchen Zweig die Steuerung einschlagen wird.

Ausdruck

Wenn condition ein Ausdruck ist, ist der von ihm gelieferte Wert der Wert des Ausdrucks, der kontextuell zu bool konvertiert wird. Wenn diese Konvertierung fehlerhaft ist, ist das Programm fehlerhaft.

Deklaration

Wenn condition eine einfache Deklaration ist, ist der von ihr gelieferte Wert der Wert der Entscheidungsvariablen (siehe unten), kontextuell konvertiert zu bool . Wenn diese Konvertierung fehlerhaft ist, ist das Programm fehlerhaft.

Nicht-strukturierte Bindungsdeklaration

Die Deklaration unterliegt folgenden Einschränkungen:

  • Syntaktisch entspricht es der folgenden Form:
  • type-specifier-seq declarator = assignment-expression
(bis C++11)
  • attribute-specifier-seq (optional) decl-specifier-seq declarator brace-or-equal-initializer
(seit C++11)

Die Entscheidungsvariable der Deklaration ist die deklarierte Variable.

Strukturierte Bindungsdeklaration

Die Deklaration unterliegt folgenden Einschränkungen:

Die Entscheidungsvariable der Deklaration ist die eingeführte Variable e , die durch die Deklaration eingeführt wird .

(seit C++26)

Zweigauswahl

Wenn die Bedingung zu true ausgewertet wird, wird statement-true ausgeführt.

Wenn der else -Teil der if -Anweisung vorhanden ist und die Bedingung false liefert, wird Anweisung-falsch ausgeführt.

Wenn der else -Teil der if -Anweisung vorhanden ist und statement-true ebenfalls eine if -Anweisung ist, dann muss diese innere if -Anweisung ebenfalls einen else -Teil enthalten (mit anderen Worten: In geschachtelten if -Anweisungen wird else dem nächsten if zugeordnet, das noch keinen zugehörigen else -Teil besitzt).

#include <iostream>
int main()
{
    // einfache if-Anweisung mit else-Zweig
    int i = 2;
    if (i > 2)
        std::cout << i << " is greater than 2\n";
    else
        std::cout << i << " is not greater than 2\n";
    // verschachtelte if-Anweisung
    int j = 1;
    if (i > 1)
        if (j > 2)
            std::cout << i << " > 1 and " << j << " > 2\n";
        else // dieses else gehört zu if (j > 2), nicht zu if (i > 1)
            std::cout << i << " > 1 and " << j << " <= 2\n";
    // Deklarationen können als Bedingungen mit dynamic_cast verwendet werden
    struct Base
    {
        virtual ~Base() {}
    };
    struct Derived : Base
    {
        void df() { std::cout << "df()\n"; }
    };
    Base* bp1 = new Base;
    Base* bp2 = new Derived;
    if (Derived* p = dynamic_cast<Derived*>(bp1)) // Cast schlägt fehl, gibt nullptr zurück
        p->df(); // nicht ausgeführt
    if (auto p = dynamic_cast<Derived*>(bp2)) // Cast erfolgreich
        p->df(); // ausgeführt
}

Ausgabe:

2 is not greater than 2
2 > 1 and 1 <= 2
df()

if Anweisungen mit Initialisierer

Wenn ein Init-Anweisung verwendet wird, entspricht die if -Anweisung

{
Init-Anweisung
attr  (optional) if constexpr (optional) ( Bedingung )
Anweisung-wahr

}

oder

{
Init-Anweisung
attr  (optional) if constexpr (optional) ( Bedingung )
Anweisung-wahr
else
Anweisung-falsch

}

Mit der Ausnahme, dass Namen, die durch die Init-Anweisung deklariert werden (falls Init-Anweisung eine Deklaration ist) und Namen, die durch Bedingung deklariert werden (falls Bedingung eine Deklaration ist), sich im selben Gültigkeitsbereich befinden, der auch der Gültigkeitsbereich beider Anweisungen ist.

std::map<int, std::string> m;
std::mutex mx;
extern bool shared_flag; // guarded by mx
int demo()
{
    if (auto it = m.find(10); it != m.end())
        return it->second.size();
    if (char buf[10]; std::fgets(buf, 10, stdin))
        m[0] += buf;
    if (std::lock_guard lock(mx); shared_flag)
    {
        unsafe_ping();
        shared_flag = false;
    }
    if (int s; int count = ReadBytesWithSignal(&s))
    {
        publish(count);
        raise(s);
    }
    if (const auto keywords = {"if", "for", "while"};
        std::ranges::any_of(keywords, [&tok](const char* kw) { return tok == kw; }))
    {
        std::cerr << "Token must not be a keyword\n";
    }
}
(seit C++17)


Constexpr if

Die Anweisung, die mit if constexpr beginnt, wird als constexpr if-Anweisung bezeichnet. Alle Unteranweisungen einer constexpr if-Anweisung sind kontrollflussbeschränkte Anweisungen .

In einer constexpr-if-Anweisung muss condition ein kontextuell umgewandelter konstanter Ausdruck vom Typ bool sein (bis C++23) ein Ausdruck sein, der kontextuell zu bool umgewandelt wird , wobei die Umwandlung ein konstanter Ausdruck ist (seit C++23) .

Wenn condition zu true ausgewertet wird, dann wird statement-false verworfen (falls vorhanden), andernfalls wird statement-true verworfen.

Die return -Anweisungen in einer verworfenen Anweisung nehmen nicht an der Ableitung des Funktionrückgabetyps teil:

template<typename T>
auto get_value(T t)
{
    if constexpr (std::is_pointer_v<T>)
        return *t; // leitet Rückgabetyp zu int für T = int* ab
    else
        return t;  // leitet Rückgabetyp zu int für T = int ab
}

Der verworfenen Anweisung kann ODR-use eine Variable verwenden, die nicht definiert ist:

extern int x; // keine Definition von x erforderlich
int f()
{
    if constexpr (true)
        return 0;
    else if (x)
        return x;
    else
        return -x;
}

Außerhalb einer Vorlage wird eine verworfenen Anweisung vollständig geprüft. if constexpr ist kein Ersatz für die #if Präprozessor-Direktive:

void f()
{
    if constexpr(false)
    {
        int i = 0;
        int *p = i; // Fehler selbst in verworfenem Statement
    }
}

Wenn eine constexpr-if-Anweisung innerhalb einer templated entity erscheint und die condition nach der Instanziierung nicht value-dependent ist, wird die verworfenen Anweisung nicht instanziiert, wenn das umschließende Template instanziiert wird.

template<typename T, typename ... Rest>
void g(T&& p, Rest&& ...rs)
{
    // ... p verarbeiten
    if constexpr (sizeof...(rs) > 0)
        g(rs...); // niemals mit einer leeren Argumentliste instanziiert
}

Die Bedingung bleibt wertabhängig nach der Instanziierung ist eine geschachtelte Vorlage:

template<class T>
void g()
{
    auto lm = [=](auto p)
    {
        if constexpr (sizeof(T) == 1 && sizeof p == 1)
        {
            // diese Bedingung bleibt wertabhängig nach der Instanziierung von g<T>,
            // was implizite Lambda-Captures beeinflusst
            // dieser Verbundblock kann erst nach der
            // Instanziierung des Lambda-Rumpfes verworfen werden
        }
    };
}

Der verworfenen Anweisung kann nicht für jede mögliche Spezialisierung fehlerhaft sein:

template<typename T>
void f()
{
    if constexpr (std::is_arithmetic_v<T>)
        // ...
    else {
        using invalid_array = int[-1]; // ungültig: für jedes T ungültig
        static_assert(false, "Must be arithmetic"); // ungültig vor CWG2518
    }
}

Die gängige Problemumgehung vor der Implementierung von CWG issue 2518 für eine solche Catch-all-Anweisung ist ein typabhängiger Ausdruck, der immer false ist:

template<typename>
constexpr bool dependent_false_v = false;
template<typename T>
void f()
{
    if constexpr (std::is_arithmetic_v<T>)
        // ...
    else {
        // Workaround vor CWG2518
        static_assert(dependent_false_v<T>, "Muss arithmetisch sein");
    }
}

Eine typedef-Deklaration oder Alias-Deklaration (seit C++23) kann als Init-Statement einer constexpr-if-Anweisung verwendet werden, um den Gültigkeitsbereich des Typ-Alias zu reduzieren.

(seit C++17)


Consteval if

Die Anweisung, die mit if consteval beginnt, wird als consteval-if-Anweisung bezeichnet. Alle Unteranweisungen einer consteval-if-Anweisung sind kontrollflussbeschränkte Anweisungen .

statement muss ein zusammengesetzter Ausdruck sein, und es wird weiterhin als Teil der consteval-if-Anweisung behandelt, selbst wenn es kein zusammengesetzter Ausdruck ist (und somit zu einem Kompilierungsfehler führt):

constexpr void f(bool b)
{
    if (true)
        if consteval {}
        else ; // Fehler: keine Verbundanweisung
               // else nicht mit äußerem if assoziiert
}

Wenn eine consteval-if-Anweisung in einem manifestly constant-evaluated context ausgewertet wird, wird compound-statement ausgeführt. Andernfalls wird statement ausgeführt, falls vorhanden.

Wenn die Anweisung mit if ! consteval beginnt, müssen sowohl die compound-statement als auch die statement (falls vorhanden) zusammengesetzte Anweisungen sein. Solche Anweisungen gelten nicht als consteval if-Anweisungen, sind jedoch äquivalent zu consteval if-Anweisungen:

  • if ! consteval { /* stmt */ } entspricht
if consteval { } else { /* stmt */ } .
  • if ! consteval { /* stmt-1 */ } else { /* stmt-2 */ } entspricht
if consteval { /* stmt-2 */ } else { /* stmt-1 */ } .

compound-statement in einer consteval if-Anweisung (oder statement in der negativen Form) befindet sich in einem immediate function context , in welchem ein Aufruf einer immediate function keine konstante Expression sein muss.

#include <cmath>
#include <cstdint>
#include <cstring>
#include <iostream>
constexpr bool is_constant_evaluated() noexcept
{
    if consteval { return true; } else { return false; }
}
constexpr bool is_runtime_evaluated() noexcept
{
    if not consteval { return true; } else { return false; }
}
consteval std::uint64_t ipow_ct(std::uint64_t base, std::uint8_t exp)
{
    if (!base) return base;
    std::uint64_t res{1};
    while (exp)
    {
        if (exp & 1) res *= base;
        exp /= 2;
        base *= base;
    }
    return res;
}
constexpr std::uint64_t ipow(std::uint64_t base, std::uint8_t exp)
{
    if consteval // verwende einen kompilierzeitfreundlichen Algorithmus
    {
        return ipow_ct(base, exp);
    }
    else // verwende Laufzeitauswertung
    {
        return std::pow(base, exp);
    }
}
int main(int, const char* argv[])
{
    static_assert(ipow(0, 10) == 0 && ipow(2, 10) == 1024);
    std::cout << ipow(std::strlen(argv[0]), 3) << '\n';
}
(seit C++23)

Hinweise

Wenn statement-true oder statement-false keine zusammengesetzte Anweisung ist, wird sie so behandelt, als ob sie es wäre:

if (x)
    int i;
// i ist nicht mehr im Gültigkeitsbereich

ist dasselbe wie

if (x)
{
    int i;
}
// i ist nicht mehr im Gültigkeitsbereich

Der Gültigkeitsbereich des Namens, der durch die condition eingeführt wird, falls es sich um eine Deklaration handelt, ist der kombinierte Gültigkeitsbereich beider Anweisungskörper:

if (int x = f())
{
    int x; // Fehler: Neudeklaration von x
}
else
{
    int x; // Fehler: Neudeklaration von x
}

Wenn statement-true durch goto oder longjmp betreten wird, wird condition nicht ausgewertet und statement-false nicht ausgeführt.

Eingebaute Konvertierungen sind in der Bedingung einer constexpr-if-Anweisung nicht erlaubt, außer nicht- einschränkenden integralen Konvertierungen zu bool .

(seit C++17)
(bis C++23)
Feature-Test-Makro Wert Std Feature
__cpp_if_constexpr 201606L (C++17) constexpr if
__cpp_if_consteval 202106L (C++23) consteval if

Schlüsselwörter

if , else , constexpr , consteval

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 631 C++98 der Kontrollfluss war nicht spezifiziert, wenn die
erste Unteranweisung über eine Marke erreicht wird
die Bedingung wird nicht ausgewertet und die zweite
Unteranweisung wird nicht ausgeführt (wie in C)

Siehe auch

erkennt, ob der Aufruf in einem konstant ausgewerteten Kontext erfolgt
(Funktion)
C-Dokumentation für if -Anweisung