Espacios de nombres
Variantes
Acciones

Instrucción if

De cppreference.com
< cpp‎ | language

Ejecuta otra instrucción de forma condicional.

Se utiliza donde necesita ejecutarse código basado en una condición en tiempo de compilación o (desde C++17) en tiempo de ejecución, o si la instrucción if se evalúa en un contexto manifiestamente evaluado constante (desde C++23).

Contenido

[editar] Sintaxis

atrib(opcional) if constexpr(opcional) ( instrucción-de-inicialización(opcional) condición ) instrucción-si-verdadero (1)
atrib(opcional) if constexpr(opcional) ( instrucción-de-inicialización(opcional) condición ) instrucción-si-verdadero else instrucción-si-falso (2)
atrib(opcional) if !(opcional) consteval instrucción-compuesta (3) (desde C++23)
atrib(opcional) if !(opcional) consteval instrucción-compuesta else instrucción (4) (desde C++23)
1) instrucción if sin un ramal else
2) instrucción if con un ramal else
3) instrucción consteval if sin un ramal else
4) instrucción consteval if con un ramal else
atrib(C++11) - cualquier número de atributos
constexpr - (desde C++17) si está presente, la instrucción se convierte en una instrucción constexpr if
instrucción-de-inicialización - (desde C++17) ya sea
(desde C++23)
Tenga en cuenta que cualquier instrucción-de-inicialización tiene que terminar con un punto y coma ;, por lo que a menudo se describe de manera informal como una expresión o declaración seguida de un punto y coma.
condición - una de
instrucción-si-verdadero - cualquier instrucción (frecuentemente una instrucción compuesta), que se ejecuta si la condición se evalúa como verdadera (true).
instrucción-si-falso - cualquier instrucción (frecuentemente una instrucción compuesta), que se ejecuta si la condición se evalúa como falsa (false).
instrucción-compuesta - cualquier instrucción compuesta, que se ejecuta si la instrucción if
instrucción - cualquier instrucción (debe ser una instrucción compuesta, ver más abajo), que se ejecuta si la instrucción if

[editar] Explicación

Si la condición produce true después de una conversión a bool, se ejecuta instrucción-si-verdadero.

Si la parte else de la instrucción if está presente y la condición produce false después de una conversión abool, se ejecuta instrucción-si-falso.

En la segunda forma de la instrucción if (la que incluye la parte else), si instrucción-si-verdadero es a su vez una instrucción if, entonces ese if interior también tiene que contener una parte else (en otras palabras, en instrucciones if anidadas, la parte else se asocia con la instrucción if más cercana que no tiene else.

#include <iostream>
 
int main() {
    // instrucción if sencilla con cláusula else
    int i = 2;
    if (i > 2) {
        std::cout << i << "es mayor que 2\n";
    } else {
        std::cout << i << " no es mayor que 2\n";
    }
 
    // instrucción if anidada
    int j = 1;
    if (i > 1)
        if (j > 2)
            std::cout << i << " > 1 y " << j << " > 2\n";
        else // este else es parte de if (j > 2), no de if (i > 1)
            std::cout << i << " > 1 y " << j << " <= 2\n";
 
   // declaraciones pueden usarse como condiciones con dynamic_cast
   struct Base {
        virtual ~Base() {}
   };
   struct Derivada : Base {
       void df() { std::cout << "df()\n"; }
   };
   Base* bp1 = new Base;
   Base* bp2 = new Derivada;
 
   if (Derivada* p = dynamic_cast<Derivada*>(bp1)) // conversión falla, devuelve nullptr
       p->df();  // no se ejecuta
 
   if (auto p = dynamic_cast<Derivada*>(bp2)) // conversión tiene éxito
       p->df();  // se ejecuta
}

Salida:

2 no es mayor que 2
2 > 1 y 1 <= 2
df()

Instrucciones if con un inicializador

Si se usa una instrucción-de-inicialización, la instrucción if es equivalente a:

{
instrucción-de-inicialización
atrib(opcional) if constexpr(opcional) ( condición )
instrucción-si-verdadero

}

o

{
instruccion-de-inicialización
atrib(opcional) if constexpr(opcional) ( condición )
instrucción-si-verdadero
else
instrucción-si-falso

}

Excepto que los nombres declarados por la instruccion-de-inicialización (siinstruccion-de-inicialización es una declaración) y los nombres declarados por la condición (la condición if es una declaración) están en el mismo ámbito, que es también el ámbito de ambas instrucciones.

std::map<int, std::string> m;
std::mutex mx;
extern bool shared_flag; // protegido por 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, [&s](const char* kw) { return s == kw; })) {
     std::cerr << "Signo no debe ser una palabra clave.\n";
   }
}
(desde C++17)


If en tiempo de compilación (constexpr if)

A la instrucción que comienza con if constexpr se le conoce como la instrucción constexpr if.

En una instrucción constexpr if, el valor de la condición tiene que ser una expresión constante de tipo bool convertida contextualmenteuna expresión convertida contextualmente a bool, donde la conversión es una expresión constante (desde C++23). Si el valor es true, entonces instrucción-si-falso se descarta (si está presente), de otra manera, instrucción-si-verdadero se descarta.

Las instrucciones return en una instrucció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 tipo de retorno como int para T = int*
    else
        return t;  // deduce tipo de retorno como int para T = int
}

La instrucción descartada puede hacer uso odr de una variable que no está definida:

extern int x; // no se requiere una definición de x
int f() {
if constexpr (true)
    return 0;
else if (x)
    return x;
else
    return -x;
}

Si una instrucción constexpr if aparece dentro de una entidad de plantilla, y si la condición no es dependiente de valor después de la creación, la instrucción descartada no se crea cuando se crea una instancia de la plantilla adjunta.

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

Fuera de una plantilla, la instrucción descartada se comprueba en su totalidad. if constexpr no es un substituto para la directiva #if del preprocesador:

void f() {
    if constexpr(false) {
        int i = 0;
        int *p = i; // ERROR aún en la instrucción descartada
    }
}


Nota: un ejemplo donde la condición permanece dependiente del valor después de la creación es una plantilla anidada. Por ejemplo:

template<class T> void g() {
    auto lm = [](auto p) {
        if constexpr (sizeof(T) == 1 && sizeof p == 1) {
           // esta condición permanece dependiente de valor después de la creación de g<T>
        }
    };
}

Nota: la instrucció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
       static_assert(false, "Tiene que ser aritmético"); // Mal formado: inválido para toda T
}

La solución alterna común para tal instrucción general de este tipo es una expresión dependiente del tipo que es siempre falsa:

template<class> inline constexpr bool dependent_false_v = false;
template <typename T>
void f() {
     if constexpr (std::is_arithmetic_v<T>)
         // ...
     else
       static_assert(dependent_false_v<T>, "Tiene que ser aritmético"); // de acuerdo
}

Las etiquetas (destinos de goto, las etiquetas case, y default:) que aparecen en una sub-instrucción de if en tiempo de compilación (constexpr if) solamente pueden ser referenciadas (por switch o goto) dentro de la misma sub-instrucción. (hasta C++23)

Nota: una declaración typedef o declaración de alias (desde C++23) se puede usar como instrucción-de-inicialización de una instrucción constexpr if para reducir el ámbito del alias de tipo.

(desde C++17)

Consteval if

Se conoce como instrucción consteval if a la instrucción que comeinza con if consteval. En una instrucción consteval if, instrucción-compuesta e instrucción (si hay) deben ser instrucciones compuestas.

Si statement no es una instrucción compuesta, todavía se tratará como parte de la instrucción consteval (y por lo que resulta es un error de compilación):

constexpr void f(bool b) {
    if (true)
        if consteval { }
        else ; // error: no una instrucción-compuesta
               // else no asignado con el if externo
}


Si una instrucción consteval if se evalúa como un contexto manifiestamente evaluado constante, se ejecuta instrucción-compuesta. Si no, se ejecuta instrucción si está presente.

Una etiqueta case o default que aparece dentro de una instrucción consteval if se asociará con una instrucción switch dentro de la misma instrucción if. Una etiqueta declarada en una subinstrucción de una instrucción consteval if solo será referenciada por una instrucción en la misma subinstrucción.

Si la instrucción comienza con if !consteval, la instrucción-compuesta y la instrucción (si hay) deben ser instrucciones compuestas. Tal instrucción no se considera como una instrucción consteval if, pero es equivalente a una instrucción consteval if:

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

instrucción-compuesta en una instrucción consteval if (o instrucción en la forma negativa) está en un contexto de función inmediata, en el que una llamada a una función inmediata no necesita ser una expresión constante.

#include <cmath>
#include <cstdint>
#include <cstring>
#include <iostream>
 
constexpr bool se_evalua_constante() noexcept {
    if consteval { return true; } else { return false; }
}
 
constexpr bool se_evalua_en_tiempo_de_ejecucion() noexcept {
    if not consteval { return true; } else { return false; }
}
 
consteval std::uint64_t pot_ent_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 pot_ent(std::uint64_t base, std::uint8_t exp) {
    if consteval { // usar un algoritmo amigable en tiempo de compilación
        return pot_ent_ct(base, exp);
    }
    else { // usar la evaluación en tiempo de ejecución
        return std::pow(base, exp);
    }
}
 
int main(int, const char* argv[]) {
    static_assert(pot_ent(0,10) == 0 && pot_ent(2,10) == 1024);
    std::cout << pot_ent(std::strlen(argv[0]), 3) << '\n';
}


(desde C++23)

[editar] Notas

Si instrucción-si-verdadero o instrucción-si-falso no son instrucciones compuestas, se tratan como si lo fueran:

if (x)
    int i;
// i ya no se encuentra en ámbito

es lo mismo que

if (x) {
    int i;
} // i ya no se encuentra en ámbito

El ámbito del nombre introducido por la condición, si es una declaración, es el ámbito combinado de los cuerpos de ambas instrucciones:

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

Si se accede a instrucción-si-verdadero mediante goto o longjmp, no se ejecuta instrucción-si-falso.

No están permitidas las conversiones en la condición de una instrucción constexpr if, excepto para las conversiones enteras no reductivas a bool.

(desde C++17)
(hasta C++23)

switch y goto no pueden saltar a una rama de la instrucción constexpr if o una instrucción consteval if (desde C++23).

(desde C++17)

[editar] Palabras clave

if, else, constexpr, consteval

[editar] Informes de defectos

Los siguientes informes de defectos de cambio de comportamiento se aplicaron de manera retroactiva a los estándares de C++ publicados anteriormente.

ID Aplicado a Comportamiento según lo publicado Comportamiento correcto
CWG 631 C++98 No se especificaba en flujo de control si la primera subinstrucción se alcanzaba a través de una etiqueta. Igual que en C.

[editar] Véase también

Detecta si la llamada ocurre dentro de un contexto evaluado constante.
(función) [editar]
Documentación de C para instrucción if