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

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)
1) if instruction sans branche else
2) if instruction avec une branche else
3) Instruction if consteval sans branche else
4) Instruction consteval if avec une branche else
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
(depuis C++23)

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 :
  • type-specifier-seq declarator = assignment-expression
(jusqu'à C++11)
  • attribute-specifier-seq (optionnel) decl-specifier-seq declarator brace-or-equal-initializer
(depuis C++11)

La variable de décision de la déclaration est la variable déclarée.

Déclaration de liaison structurée

La déclaration présente les restrictions suivantes :

  • L' expression dans son initialiseur ne peut pas être de type tableau.
  • La séquence de spécificateurs de déclaration ne peut contenir que des spécificateurs de type et constexpr .

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 initialiseur

Si une init-statement est utilisée, l'instruction if est équivalente à

{
init-statement
attr  (optionnel) if constexpr (optionnel) ( condition )
statement-true

}

ou

{
init-statement
attr  (optionnel) if constexpr (optionnel) ( condition )
statement-true
else
statement-false

}

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 if

L'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 si

L'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) :

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 :

  • if ! consteval { /* stmt */ } est équivalent à
if consteval { } else { /* stmt */ } .
**Note:** Le code C++ présent dans les balises n'a pas été traduit conformément aux instructions, car il s'agit de code de programmation. Seul le texte en dehors des balises de code aurait dû être traduit, mais dans cet extrait, il n'y a que du code C++ et aucun texte libre à traduire.
  • if ! consteval { /* stmt-1 */ } else { /* stmt-2 */ } est équivalent à
if consteval { /* stmt-2 */ } else { /* stmt-1 */ } .
**Note:** Le code C++ n'a pas été traduit car il se trouve dans des balises spécifiques au code et contient des termes techniques C++ qui doivent rester en anglais pour des raisons de précision technique. Seul le point final a été conservé tel quel.

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.

#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

détecte si l'appel se produit dans un contexte d'évaluation constante
(fonction)
Documentation C pour if statement