if
statement
Exécute conditionnellement une autre instruction.
Utilisé lorsque du code doit être exécuté en fonction d'une condition , ou selon que l'instruction if est évaluée dans un contexte manifestement constant (depuis C++23) .
Table des matières |
Syntaxe
attr
(optionnel)
if
constexpr
(optionnel)
(
init-statement
(optionnel)
condition
)
statement-true
|
(1) | ||||||||
attr
(optionnel)
if
constexpr
(optionnel)
(
init-statement
(optionnel)
condition
)
statement-true
else
statement-false
|
(2) | ||||||||
attr
(optionnel)
if
!
(optionnel)
consteval
compound-statement
|
(3) | (depuis C++23) | |||||||
attr
(optionnel)
if
!
(optionnel)
consteval
compound-statement
else
statement
|
(4) | (depuis C++23) | |||||||
| attr | - | (depuis C++11) n'importe quel nombre d' attributs | ||
constexpr
|
- | (depuis C++17) si présent, l'instruction devient une instruction constexpr if | ||
| init-statement | - |
(depuis C++17)
soit
Notez que toute init-statement doit se terminer par un point-virgule. C'est pourquoi elle est souvent décrite informellement comme une expression ou une déclaration suivie d'un point-virgule. |
||
| condition | - | une condition | ||
| statement-true | - | l' instruction à exécuter si condition donne true | ||
| statement-false | - | l'instruction à exécuter si condition donne false | ||
| compound-statement | - |
l'
instruction composée
à exécuter si l'instruction
if
est évaluée dans un
contexte manifestement constant
(ou n'est pas évaluée dans un tel contexte si
!
précède
consteval
)
|
||
| statement | - |
l'instruction (doit être une instruction composée, voir
ci-dessous
) à exécuter si l'instruction
if
n'est pas évaluée dans un contexte manifestement constant (ou est évaluée dans un tel contexte si
!
précède
consteval
)
|
Condition
Une condition peut être soit une expression soit une déclaration simple .
|
(depuis C++26) |
- S'il peut être résolu syntaxiquement comme une expression, il est traité comme une expression. Sinon, il est traité comme une déclaration qui n'est pas une déclaration de décomposition structurée (depuis C++26) .
Lorsque le contrôle atteint la condition, celle-ci produira une valeur, qui est utilisée pour déterminer vers quelle branche le contrôle s'orientera.
Expression
Si condition est une expression, la valeur qu'elle produit est la valeur de l'expression convertie contextuellement en bool . Si cette conversion est mal formée, le programme est mal formé.
Déclaration
Si condition est une déclaration simple, la valeur qu'elle produit est la valeur de la variable de décision (voir ci-dessous) convertie contextuellement en bool . Si cette conversion est mal formée, le programme est mal formé.
Déclaration non structurée de liaison
La déclaration comporte les restrictions suivantes :
- Conforme syntaxiquement à la forme suivante :
|
(jusqu'à C++11) |
|
(depuis C++11) |
- Le déclarateur ne peut pas spécifier une fonction ou un tableau .
- La séquence de spécificateurs de type (jusqu'en C++11) séquence de spécificateurs de déclaration ne peut contenir que des spécificateurs de type et constexpr , et elle (depuis C++11) ne peut pas définir une classe ou une énumération .
La variable de décision de la déclaration est la variable déclarée.
Déclaration de liaison structuréeLa déclaration présente les restrictions suivantes :
La variable de décision de la déclaration est la variable inventée e introduite par la déclaration . |
(depuis C++26) |
Sélection de branche
Si la condition donne true , instruction-vraie est exécutée.
Si la partie else de l'instruction if est présente et que la condition donne false , instruction-false est exécutée.
Si la partie else de l'instruction if est présente et que statement-true est également une instruction if , alors cette instruction if interne doit également contenir une partie else (en d'autres termes, dans les instructions if imbriquées, le else est associé au if le plus proche qui n'a pas encore de else associé).
#include <iostream> int main() { // instruction if simple avec une clause else int i = 2; if (i > 2) std::cout << i << " is greater than 2\n"; else std::cout << i << " is not greater than 2\n"; // instruction if imbriquée int j = 1; if (i > 1) if (j > 2) std::cout << i << " > 1 and " << j << " > 2\n"; else // ce else fait partie de if (j > 2), pas de if (i > 1) std::cout << i << " > 1 and " << j << " <= 2\n"; // les déclarations peuvent être utilisées comme conditions avec 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)) // le cast échoue, retourne nullptr p->df(); // non exécuté if (auto p = dynamic_cast<Derived*>(bp2)) // le cast réussit p->df(); // exécuté }
Sortie :
2 is not greater than 2 2 > 1 and 1 <= 2 df()
if instructions avec initialiseurSi une init-statement est utilisée, l'instruction if est équivalente à
ou
Sauf que les noms déclarés par l' init-statement (si l' init-statement est une déclaration) et les noms déclarés par la condition (si la condition est une déclaration) sont dans la même portée, qui est également la portée des deux statement s. std::map<int, std::string> m; std::mutex mx; extern bool shared_flag; // protégé par 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 << "Le jeton ne doit pas être un mot-clé\n"; } } |
(depuis C++17) | ||||||||||||||||||||||||||||||||||||||||||||||
Constexpr ifL'instruction qui commence par if constexpr est connue sous le nom d' instruction constexpr if . Toutes les sous-instructions d'une instruction constexpr if sont des instructions à contrôle de flux limité . Dans une instruction constexpr if, condition doit être une expression constante contextuellement convertie de type bool (jusqu'à C++23) une expression contextuellement convertie en bool , où la conversion est une expression constante (depuis C++23) . Si condition donne true , alors statement-false est ignoré (s'il est présent), sinon, statement-true est ignoré. Les return dans une instruction rejetée ne participent pas à la déduction du type de retour de fonction : template<typename T> auto get_value(T t) { if constexpr (std::is_pointer_v<T>) return *t; // déduit le type de retour comme int pour T = int* else return t; // déduit le type de retour comme int pour T = int } L'instruction écartée peut ODR-utiliser une variable qui n'est pas définie : extern int x; // aucune définition de x requise int f() { if constexpr (true) return 0; else if (x) return x; else return -x; } En dehors d'un modèle, une instruction écartée est entièrement vérifiée. if constexpr n'est pas un substitut pour la directive de préprocesseur #if : void f() { if constexpr(false) { int i = 0; int *p = i; // Erreur même dans une instruction rejetée } } Si une instruction constexpr if apparaît à l'intérieur d'une entité template , et si la condition n'est pas value-dependent après l'instanciation, l'instruction rejetée n'est pas instanciée lorsque le template englobant est instancié. template<typename T, typename ... Rest> void g(T&& p, Rest&& ...rs) { // ... traiter p if constexpr (sizeof...(rs) > 0) g(rs...); // jamais instancié avec une liste d'arguments vide } La condition reste dépendante de la valeur après l'instanciation est un modèle imbriqué : template<class T> void g() { auto lm = [=](auto p) { if constexpr (sizeof(T) == 1 && sizeof p == 1) { // cette condition reste dépendante de la valeur après l'instanciation de g<T>, // ce qui affecte les captures implicites du lambda // ce bloc d'instructions peut être écarté seulement après // l'instanciation du corps du lambda } }; } L'instruction rejetée ne peut pas être mal formée pour toute spécialisation possible : template<typename T> void f() { if constexpr (std::is_arithmetic_v<T>) // ... else { using invalid_array = int[-1]; // mal formé : invalide pour tout T static_assert(false, "Must be arithmetic"); // mal formé avant CWG2518 } } La solution de contournement courante avant l'implémentation de CWG issue 2518 pour une telle instruction catch-all est une expression dépendante du type qui est toujours false : template<typename> constexpr bool dependent_false_v = false; template<typename T> void f() { if constexpr (std::is_arithmetic_v<T>) // ... else { // contournement avant CWG2518 static_assert(dependent_false_v<T>, "Doit être arithmétique"); } } Une déclaration typedef ou déclaration d'alias (depuis C++23) peut être utilisée comme init-statement d'une instruction constexpr if pour réduire la portée de l'alias de type.
|
(depuis C++17) |
Consteval siL'instruction qui commence par if consteval est connue sous le nom d' instruction consteval if . Toutes les sous-instructions d'une instruction consteval if sont des instructions à contrôle de flux limité . statement doit être une instruction composée, et elle sera toujours traitée comme faisant partie de l'instruction consteval if même si ce n'est pas une instruction composée (et entraîne donc une erreur de compilation) :
Exécuter ce code
constexpr void f(bool b) { if (true) if consteval {} else ; // erreur : pas une instruction composée // else non associé au if externe } Si une instruction if consteval est évaluée dans un contexte manifestement constant-évalué , compound-statement est exécuté. Sinon, statement est exécuté s'il est présent. Si l'instruction commence par if ! consteval , la compound-statement et l' statement (le cas échéant) doivent toutes deux être des instructions composées. Ces instructions ne sont pas considérées comme des instructions consteval if, mais sont équivalentes aux instructions consteval if :
compound-statement dans une instruction consteval if (ou statement dans la forme négative) se trouve dans un contexte de fonction immédiate , dans lequel un appel à une fonction immédiate n'a pas besoin d'être une expression constante.
Exécuter ce code
#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 // utiliser un algorithme adapté à la compilation { return ipow_ct(base, exp); } else // utiliser l'évaluation à l'exécution { 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'; } |
(depuis C++23) |
Notes
Si statement-true ou statement-false n'est pas une instruction composée, elle est traitée comme si elle l'était :
if (x) int i; // i n'est plus dans la portée
est le même que
if (x) { int i; } // i n'est plus dans la portée
La portée du nom introduit par condition , s'il s'agit d'une déclaration, est la portée combinée des deux corps d'instructions :
if (int x = f()) { int x; // erreur : redéclaration de x } else { int x; // erreur : redéclaration de x }
Si
statement-true
est atteint par
goto
ou
longjmp
,
condition
n'est pas évaluée et
statement-false
n'est pas exécuté.
|
Les conversions intégrées ne sont pas autorisées dans la condition d'une instruction constexpr if, sauf pour les conversions intégrales non-rétrécissantes vers bool . |
(depuis C++17)
(jusqu'à C++23) |
| Macro de test de fonctionnalité | Valeur | Std | Fonctionnalité |
|---|---|---|---|
__cpp_if_constexpr
|
201606L
|
(C++17) |
constexpr
if
|
__cpp_if_consteval
|
202106L
|
(C++23) |
consteval
if
|
Mots-clés
if , else , constexpr , consteval
Rapports de défauts
Les rapports de défauts modifiant le comportement suivants ont été appliqués rétroactivement aux normes C++ précédemment publiées.
| DR | Appliqué à | Comportement tel que publié | Comportement correct |
|---|---|---|---|
| CWG 631 | C++98 |
le flux de contrôle n'était pas spécifié si la
première sous-instruction est atteinte via une étiquette |
la condition n'est pas évaluée et la deuxième
sous-instruction n'est pas exécutée (identique au C) |
Voir aussi
|
(C++20)
|
détecte si l'appel se produit dans un contexte d'évaluation constante
(fonction) |
|
Documentation C
pour
if
statement
|
|