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

Ejecuta condicionalmente otra sentencia.

Se utiliza cuando el código necesita ejecutarse basándose en una condición , o si la if se evalúa en un contexto manifiestamente constante (since C++23) .

Contenidos

Sintaxis

attr  (opcional) if constexpr (opcional)
( init-statement  (opcional) condition ) statement-true
(1)
attr  (opcional) if constexpr (opcional)
( init-statement  (opcional) condition ) statement-true else statement-false
(2)
attr  (opcional) if ! (opcional) consteval compound-statement (3) (desde C++23)
attr  (opcional) if ! (opcional) consteval compound-statement else statement (4) (desde C++23)
1) if sentencia sin una rama else
2) if sentencia con una rama else
3) sentencia consteval if sin una rama else
4) sentencia consteval if con una rama else
attr - (desde C++11) cualquier número de atributos
constexpr - (desde C++17) si está presente, la sentencia se convierte en una sentencia constexpr if
init-statement - (desde C++17) ya sea
(desde C++23)

Nótese que cualquier init-statement debe terminar con un punto y coma. Por esto se describe informalmente como una expresión o declaración seguida de un punto y coma.

condition - una condición
statement-true - la sentencia a ejecutar si condition devuelve true
statement-false - la sentencia a ejecutar si condition devuelve false
compound-statement - la sentencia compuesta a ejecutar si la sentencia if se evalúa en un contexto de evaluación manifiestamente constante (o no se evalúa en dicho contexto si ! precede a consteval )
statement - la sentencia (debe ser una sentencia compuesta, ver abajo ) a ejecutar si la sentencia if no se evalúa en un contexto de evaluación manifiestamente constante (o se evalúa en dicho contexto si ! precede a consteval )

Condición

Una condición puede ser una expresión o una declaración simple .

  • Si puede resolverse sintácticamente como una declaración de structured binding , se interpreta como una declaración de structured binding.
(since C++26)
  • Si puede resolverse sintácticamente como una expresión, se trata como una expresión. De lo contrario, se trata como una declaración que no es una declaración de enlace estructurado (since C++26) .

Cuando el control alcanza la condición, la condición producirá un valor, que se utiliza para determinar a qué rama irá el control.

Expresión

Si condition es una expresión, el valor que produce es el valor de la expresión convertido contextualmente a bool . Si esa conversión es incorrecta, el programa está mal formado.

Declaración

Si condition es una declaración simple, el valor que produce es el valor de la variable de decisión (ver más abajo) convertido contextualmente a bool . Si esa conversión es incorrecta, el programa está mal formado.

Declaración de enlace no estructurado

La declaración tiene las siguientes restricciones:

  • Sintácticamente se ajusta a la siguiente forma:
  • type-specifier-seq declarator = assignment-expression
(hasta C++11)
  • attribute-specifier-seq (opcional) decl-specifier-seq declarator brace-or-equal-initializer
(desde C++11)

La variable de decisión de la declaración es la variable declarada.

Declaración de enlace estructurado

La declaración tiene las siguientes restricciones:

  • La expresión en su inicializador no puede ser de tipo array.
  • La secuencia de especificadores de declaración solo puede contener especificadores de tipo y constexpr .

La variable de decisión de la declaración es la variable inventada e introducida por la declaración .

(desde C++26)

Selección de rama

Si la condición produce true , sentencia-verdadera se ejecuta.

Si la else parte de la if sentencia está presente y la condición devuelve false , sentencia-falsa se ejecuta.

Si la parte else de la sentencia if está presente y statement-true también es una sentencia if , entonces esa sentencia if interna debe contener también una parte else (en otras palabras, en sentencias if anidadas, el else se asocia con el if más cercano que aún no tiene un else asociado).

#include <iostream>
int main()
{
    // sentencia if simple con cláusula 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";
    // sentencia if anidada
    int j = 1;
    if (i > 1)
        if (j > 2)
            std::cout << i << " > 1 and " << j << " > 2\n";
        else // este else pertenece a if (j > 2), no a if (i > 1)
            std::cout << i << " > 1 and " << j << " <= 2\n";
    // las declaraciones pueden usarse como condiciones con 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)) // el cast falla, retorna nullptr
        p->df(); // no se ejecuta
    if (auto p = dynamic_cast<Derived*>(bp2)) // el cast tiene éxito
        p->df(); // se ejecuta
}

Salida:

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

if sentencias con inicializador

Si se utiliza init-statement , la if sentencia es equivalente a

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

}

o

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

}

Excepto que los nombres declarados por el init-statement (si init-statement es una declaración) y los nombres declarados por condition (si condition es una declaración) están en el mismo ámbito, que también es el ámbito de ambas statement s.

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";
    }
}
(desde C++17)


Constexpr if

La declaración que comienza con if constexpr se conoce como constexpr if statement . Todas las subdeclaraciones de una constexpr if statement son control-flow-limited statements .

En una sentencia constexpr if, condition debe ser una expresión constante convertida contextualmente de tipo bool (hasta C++23) una expresión convertida contextualmente a bool , donde la conversión es una expresión constante (desde C++23) .

Si condición produce true , entonces sentencia-falsa se descarta (si está presente), de lo contrario, sentencia-verdadera se descarta.

Las return statements en una declaración descartada no participan en la deducción del tipo de retorno de la función:

template<typename T>
auto get_value(T t)
{
    if constexpr (std::is_pointer_v<T>)
        return *t; // deduce el tipo de retorno como int para T = int*
    else
        return t;  // deduce el tipo de retorno como int para T = int
}

La declaración descartada puede ODR-use una variable que no está definida:

extern int x; // no definition of x required
int f()
{
    if constexpr (true)
        return 0;
    else if (x)
        return x;
    else
        return -x;
}

Fuera de una plantilla, una declaración descartada se verifica completamente. if constexpr no es un sustituto de la directiva de preprocesamiento #if :

void f()
{
    if constexpr(false)
    {
        int i = 0;
        int *p = i; // Error incluso en una sentencia descartada
    }
}

Si una sentencia constexpr if aparece dentro de una entidad con plantilla , y si la condición no es value-dependent después de la instanciación, la sentencia descartada no se instancia cuando se instancia la plantilla contenedora.

template<typename T, typename ... Rest>
void g(T&& p, Rest&& ...rs)
{
    // ... manejar p
    if constexpr (sizeof...(rs) > 0)
        g(rs...); // nunca se instancia con una lista de argumentos vacía
}

La condición permanece dependiente del valor después de la instanciación es una plantilla anidada:

template<class T>
void g()
{
    auto lm = [=](auto p)
    {
        if constexpr (sizeof(T) == 1 && sizeof p == 1)
        {
            // esta condición permanece dependiente de valores después de la instanciación de g<T>,
            // lo cual afecta las capturas implícitas de lambda
            // esta sentencia compuesta puede descartarse solo después de
            // la instanciación del cuerpo de lambda
        }
    };
}

La declaración descartada no puede estar mal formada para cada especialización posible:

template<typename T>
void f()
{
    if constexpr (std::is_arithmetic_v<T>)
        // ...
    else {
        using invalid_array = int[-1]; // mal formado: inválido para todo T
        static_assert(false, "Must be arithmetic"); // mal formado antes de CWG2518
    }
}

La solución común antes de la implementación de CWG issue 2518 para tal declaración catch-all es una expresión dependiente del tipo que siempre es false :

template<typename>
constexpr bool dependent_false_v = false;
template<typename T>
void f()
{
    if constexpr (std::is_arithmetic_v<T>)
        // ...
    else {
        // solución alternativa antes de CWG2518
        static_assert(dependent_false_v<T>, "Must be arithmetic");
    }
}

Una declaración typedef o declaración de alias (desde C++23) puede usarse como la init-statement de una sentencia constexpr if para reducir el alcance del alias de tipo.

(desde C++17)


Consteval if

La declaración que comienza con if consteval se conoce como declaración consteval if . Todas las subdeclaraciones de una declaración consteval if son declaraciones de control de flujo limitado .

statement debe ser una sentencia compuesta, y seguirá siendo tratada como parte de la sentencia if consteval incluso si no es una sentencia compuesta (y por lo tanto resulta en un error de compilación):

constexpr void f(bool b)
{
    if (true)
        if consteval {}
        else ; // error: not a compound-statement
               // else not associated with outer if
}

Si una sentencia if consteval se evalúa en un contexto manifiestamente constante-evaluado , compound-statement se ejecuta. De lo contrario, statement se ejecuta si está presente.

Si la declaración comienza con if ! consteval , el compound-statement y la statement (si existe) deben ser ambas declaraciones compuestas. Estas declaraciones no se consideran consteval if statements, pero son equivalentes a consteval if statements:

  • if ! consteval { /* stmt */ } es equivalente a
if consteval { } else { /* stmt */ } .
  • if ! consteval { /* stmt-1 */ } else { /* stmt-2 */ } es equivalente a
if consteval { /* stmt-2 */ } else { /* stmt-1 */ } .

compound-statement en una sentencia consteval if (o statement en la forma negativa) está en un contexto de función inmediata , en el cual una llamada a una función inmediata no necesita ser una expresión 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 // usar un algoritmo amigable para tiempo de compilación
    {
        return ipow_ct(base, exp);
    }
    else // usar evaluación en tiempo de ejecución
    {
        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';
}
(desde C++23)

Notas

Si statement-true o statement-false no es una instrucción compuesta, se trata como si lo fuera:

if (x)
    int i;
// i ya no está en alcance

es lo mismo que

if (x)
{
    int i;
}
// i ya no está en el ámbito

El alcance del nombre introducido por condition , si es una declaración, es el alcance combinado de los cuerpos de ambas sentencias:

if (int x = f())
{
    int x; // error: redeclaración de x
}
else
{
    int x; // error: redeclaración de x
}

Si statement-true es ingresado mediante goto o longjmp , condition no es evaluado y statement-false no es ejecutado.

No se permiten conversiones incorporadas en la condición de una sentencia constexpr if, excepto para conversiones no restrictivas integrales a bool .

(desde C++17)
(hasta C++23)
Macro de prueba de características Valor Estándar Característica
__cpp_if_constexpr 201606L (C++17) constexpr if
__cpp_if_consteval 202106L (C++23) consteval if

Palabras clave

if , else , constexpr , consteval

Informes de defectos

Los siguientes informes de defectos que modifican el comportamiento se aplicaron retroactivamente a los estándares publicados anteriormente de C++.

DR Aplicado a Comportamiento publicado Comportamiento correcto
CWG 631 C++98 el flujo de control no estaba especificado si la
primera subdeclaración se alcanzaba mediante una etiqueta
la condición no se evalúa y la segunda
subdeclaración no se ejecuta (igual que en C)

Véase también

detecta si la llamada ocurre dentro de un contexto de evaluación constante
(función)
Documentación de C para if statement