if Anweisung
Führt bedingt eine andere Anweisung aus.
Wird verwendet, wenn Code basierend auf einer Bedingung ausgeführt werden muss oder ob die if Anweisung in einem manifest konstant ausgewerteten Kontext ausgewertet wird(seit C++23).
Inhalt |
[bearbeiten] 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) | |||||||
| attr | - | (seit C++11) eine beliebige Anzahl von Attributen | ||
constexpr
|
- | (seit C++17) falls vorhanden, wird die Anweisung zu einer constexpr if Anweisung | ||
| init-statement | - | (seit C++17) entweder
Beachten Sie, dass jede init-statement mit einem Semikolon enden muss. Deshalb wird sie oft informell als Ausdruck oder Deklaration gefolgt von einem Semikolon beschrieben. | ||
| condition | - | eine Bedingung | ||
| statement-true | - | die Anweisung, die ausgeführt wird, wenn die condition true ergibt | ||
| statement-false | - | die Anweisung, die ausgeführt wird, wenn die condition false ergibt | ||
| compound-statement | - | die zusammengesetzte Anweisung, die ausgeführt wird, wenn die if Anweisung in einem manifest konstant ausgewerteten Kontext ausgewertet wird (oder nicht in einem solchen Kontext ausgewertet wird, wenn ! vor consteval steht) | ||
| Anweisung | - | die Anweisung (muss eine zusammengesetzte Anweisung sein, siehe unten), die ausgeführt wird, wenn die if Anweisung nicht in einem manifest konstant ausgewerteten Kontext ausgewertet wird (oder in einem solchen Kontext ausgewertet wird, wenn ! vor consteval steht) |
[bearbeiten] Bedingung
Eine condition kann entweder ein Ausdruck oder eine einfache Deklaration sein.
|
(seit C++26) |
- Wenn sie syntaktisch als Ausdruck aufgelöst werden kann, wird sie als Ausdruck behandelt. Andernfalls wird sie als Deklaration behandelt, die keine strukturierte Bindungsdeklaration ist(seit C++26).
Wenn der Kontrollfluss auf die Bedingung trifft, liefert die Bedingung einen Wert, der verwendet wird, um zu bestimmen, zu welchem Zweig der Kontrollfluss geht.
[bearbeiten] Ausdruck
Wenn condition ein Ausdruck ist, ist der gelieferte Wert der Wert des Ausdrucks, der kontextuell zu bool konvertiert wird. Wenn diese Konvertierung fehlerhaft ist, ist das Programm fehlerhaft.
[bearbeiten] Deklaration
Wenn condition eine einfache Deklaration ist, ist der gelieferte Wert der Wert der Entscheidungsvariable (siehe unten), kontextuell zu bool konvertiert. Wenn diese Konvertierung fehlerhaft ist, ist das Programm fehlerhaft.
[bearbeiten] Nicht-strukturierte Bindungsdeklaration
Die Deklaration hat folgende Einschränkungen
- Folgt syntaktisch der folgenden Form
|
(bis C++11) |
|
(seit C++11) |
- Der Deklarator darf keine Funktion oder ein Array angeben.
- Die Typ-Spezifizierer-Sequenz(bis C++11)Deklarations-Spezifizierer-Sequenz darf nur Typ-Spezifizierer und constexpr enthalten, und sie(seit C++11) darf keine Klasse oder Aufzählung definieren.
Die Entscheidungsvariable der Deklaration ist die deklarierte Variable.
Strukturierte BindungsdeklarationDie Deklaration hat folgende Einschränkungen
Die Entscheidungsvariable der Deklaration ist die erfundene Variable e, die durch die Deklaration eingeführt wird. |
(seit C++26) |
[bearbeiten] Zweigwahl
Wenn die condition true ergibt, wird statement-true ausgeführt.
Wenn der else Teil der if Anweisung vorhanden ist und die condition false ergibt, wird statement-false 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 verschachtelten if Anweisungen wird das else mit dem nächstgelegenen if assoziiert, das noch kein assoziiertes else hat).
#include <iostream> int main() { // simple if-statement with an else clause int i = 2; if (i > 2) std::cout << i << " is greater than 2\n"; else std::cout << i << " is not greater than 2\n"; // nested if-statement int j = 1; if (i > 1) if (j > 2) std::cout << i << " > 1 and " << j << " > 2\n"; else // this else is part of if (j > 2), not of if (i > 1) std::cout << i << " > 1 and " << j << " <= 2\n"; // declarations can be used as conditions with dynamic_cast 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 fails, returns nullptr p->df(); // not executed if (auto p = dynamic_cast<Derived*>(bp2)) // cast succeeds p->df(); // executed }
Ausgabe
2 is not greater than 2 2 > 1 and 1 <= 2 df()
if Anweisungen mit InitialisiererWenn init-statement verwendet wird, ist die if Anweisung äquivalent zu
or
Mit der Ausnahme, dass Namen, die durch die init-statement deklariert wurden (wenn init-statement eine Deklaration ist) und Namen, die durch die condition deklariert wurden (wenn condition eine Deklaration ist), sich im selben Gültigkeitsbereich befinden, der auch der Gültigkeitsbereich beider statement s 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 ifDie 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 konvertierter konstanter Ausdruck vom Typ bool sein(bis C++23)ein Ausdruck, der kontextuell zu bool konvertiert wird, wobei die Konvertierung ein konstanter Ausdruck ist(seit C++23). Wenn condition true ergibt, wird statement-false verworfen (falls vorhanden), andernfalls wird statement-true verworfen. Die return Anweisungen in einer verworfenen Anweisung nehmen nicht an der Rückgabetyperkennung der Funktion teil. template<typename T> auto get_value(T t) { if constexpr (std::is_pointer_v<T>) return *t; // deduces return type to int for T = int* else return t; // deduces return type to int for T = int } Die verworfene Anweisung kann eine Variable ODR-verwenden, die nicht definiert ist. extern int x; // no definition of x required int f() { if constexpr (true) return 0; else if (x) return x; else return -x; } Außerhalb einer Vorlage wird eine verworfene 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; // Error even though in discarded statement } } Wenn eine constexpr if Anweisung innerhalb einer Vorlageneinheit erscheint und wenn condition nach der Instanziierung nicht wertabhängig ist, wird die verworfene Anweisung nicht instanziiert, wenn die umschließende Vorlage instanziiert wird. template<typename T, typename ... Rest> void g(T&& p, Rest&& ...rs) { // ... handle p if constexpr (sizeof...(rs) > 0) g(rs...); // never instantiated with an empty argument list } Die Bedingung bleibt wertabhängig nach der Instanziierung ist eine verschachtelte Vorlage. template<class T> void g() { auto lm = [=](auto p) { if constexpr (sizeof(T) == 1 && sizeof p == 1) { // this condition remains value-dependent after instantiation of g<T>, // which affects implicit lambda captures // this compound statement may be discarded only after // instantiation of the lambda body } }; } Die verworfene Anweisung darf 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]; // ill-formed: invalid for every T static_assert(false, "Must be arithmetic"); // ill-formed before CWG2518 } } Der übliche Workaround vor der Implementierung von CWG Issue 2518 für eine solche Catch-all-Anweisung ist ein typenabhä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 before CWG2518 static_assert(dependent_false_v<T>, "Must be arithmetic"); } } 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 ifDie Anweisung, die mit if consteval beginnt, wird als *consteval if Anweisung* bezeichnet. Alle Unteranweisungen einer consteval if Anweisung sind kontrollflussbeschränkte Anweisungen. statement muss eine zusammengesetzte Anweisung sein, und sie wird immer noch als Teil der consteval if Anweisung behandelt, auch wenn sie keine zusammengesetzte Anweisung ist (und somit zu einem Kompilierungsfehler führt). Führen Sie diesen Code aus constexpr void f(bool b) { if (true) if consteval {} else ; // error: not a compound-statement // else not associated with outer if } Wenn eine consteval if Anweisung in einem manifest konstant ausgewerteten Kontext ausgewertet wird, wird compound-statement ausgeführt. Andernfalls wird statement ausgeführt, wenn es vorhanden ist. Wenn die Anweisung mit if !consteval beginnt, müssen compound-statement und statement (falls vorhanden) beide zusammengesetzte Anweisungen sein. Solche Anweisungen werden nicht als consteval if Anweisungen betrachtet, sind aber äquivalent zu consteval if Anweisungen.
compound-statement in einer consteval if Anweisung (oder statement in der negativen Form) befindet sich in einem sofortigen Funktionskontext, in dem ein Aufruf einer sofortigen Funktion keine konstante Anweisung sein muss. Führen Sie diesen Code aus #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 // use a compile-time friendly algorithm { return ipow_ct(base, exp); } else // use runtime evaluation { 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) |
[bearbeiten] Hinweise
Wenn statement-true oder statement-false keine zusammengesetzte Anweisung ist, wird sie behandelt, als wäre sie
if (x) int i; // i is no longer in scope
ist dasselbe wie
if (x) { int i; } // i is no longer in scope
Der Gültigkeitsbereich des Namens, der von condition eingeführt wird, falls es sich um eine Deklaration handelt, ist der kombinierte Gültigkeitsbereich der Körper beider Anweisungen.
if (int x = f()) { int x; // error: redeclaration of x } else { int x; // error: redeclaration of x }
Wenn statement-true durch goto oder longjmp erreicht wird, wird condition nicht ausgewertet und statement-false nicht ausgeführt.
|
Integrierte Konvertierungen sind in der condition einer constexpr if Anweisung nicht erlaubt, außer für nicht-verengende Ganzzahlkonvertierungen zu bool. |
(seit C++17) (bis C++23) |
| Feature-Testmakro | Wert | Std | Feature |
|---|---|---|---|
__cpp_if_constexpr |
201606L |
(C++17) | constexpr if |
__cpp_if_consteval |
202106L |
(C++23) | consteval if |
[bearbeiten] Schlüsselwörter
if, else, constexpr, consteval
[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 631 | C++98 | der Kontrollfluss war nicht spezifiziert, wenn die erste Unteranweisung über ein Label erreicht wird |
die Bedingung nicht ausgewertet und die zweite Unteranweisung nicht ausgeführt wird (wie in C) |
[bearbeiten] Siehe auch
| (C++20) |
erkennt, ob der Aufruf innerhalb eines konstant ausgewerteten Kontexts erfolgt (Funktion) |
| C-Dokumentation für if Anweisung
| |