Instrucción if
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) | |||||||
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
| ||
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 inicializadorSi se usa una instrucción-de-inicialización, la instrucción if es equivalente a:
o
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 En una instrucción constexpr if, el valor de la condición tiene que ser una expresión constante de tipo 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 } }
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 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 ifSe conoce como instrucción consteval if a la instrucción que comeinza con 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): Ejecuta este código 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 Si la instrucción comienza con
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. Ejecuta este código #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) |
|
(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
(C++20) |
Detecta si la llamada ocurre dentro de un contexto evaluado constante. (función) |
Documentación de C para instrucción if
|