Constant expressions
Define una expresión que puede ser evaluada en tiempo de compilación.
Tales expresiones pueden utilizarse como argumentos de plantilla constantes, tamaños de array, y en otros contextos que requieran expresiones constantes, p. ej.
int n = 1; std::array<int, n> a1; // Error: “n” no es una expresión constante const int cn = 2; std::array<int, cn> a2; // OK: “cn” es una expresión constante
Definición
|
Una expresión que pertenezca a cualquiera de las categorías de expresión constante enumeradas a continuación es una expresión constante .
|
(hasta C++11) | ||
|
Las siguientes expresiones se denominan colectivamente expresiones constantes :
|
(desde C++11)
(hasta C++14) |
||
|
Las siguientes entidades son resultados permitidos de una expresión constante :
Una expresión constante es un glvalue expresión constante central que se refiere a una entidad que es un resultado permitido de una expresión constante, o una prvalue expresión constante central cuyo valor satisface las siguientes restricciones:
|
(desde C++14)
(hasta C++26) |
||
|
Una expresión constante es un glvalue expresión constante del núcleo que se refiere a un objeto o una función no inmediata , o un prvalue expresión constante del núcleo cuyo valor satisface las siguientes restricciones:
|
(desde C++26) |
Al determinar si una expresión es una expresión constante, copy elision se asume que no se realiza.
La definición de C++98 de expresiones constantes está completamente dentro del cuadro colapsable. La siguiente descripción se aplica a C++11 y versiones posteriores de C++.
Tipo literal
Los siguientes tipos se denominan colectivamente literal types :
- posiblemente calificado cv void
- tipo escalar
- tipo referencia
- un array de tipo literal
- tipo de clase posiblemente calificado cv que satisface todas las siguientes condiciones:
-
- Tiene un trivial destructor (until C++20) constexpr destructor (since C++20) .
- Todos sus miembros de datos no estáticos no variantes y clases base son de tipos literales no volátiles.
- Es uno de los siguientes tipos:
|
(desde C++17) |
-
-
- un tipo de unión agregado que satisface una de las siguientes condiciones:
-
- No tiene ningún miembro variante .
- Tiene al menos un miembro variante de tipo literal no volátil.
- un tipo agregado no unión, y cada uno de sus miembros de unión anónima satisface una de las siguientes condiciones:
-
- No tiene ningún miembro variante.
- Tiene al menos un miembro variante de tipo literal no volátil.
- un tipo con al menos un constructor (plantilla) constexpr que no sea un constructor de copia o movimiento
-
Solo los objetos de tipos literales pueden crearse dentro de una expresión constante.
Expresión constante principal
Una expresión constante principal es cualquier expresión cuya evaluación no evaluaría ninguna de las siguientes construcciones del lenguaje:
| Constructo del lenguaje | Versión | Documento(s) |
|---|---|---|
el puntero
this
, excepto en una
constexpr
function
que está siendo evaluada como parte de la expresión, o cuando aparece en una expresión de acceso a miembro de clase implícita o explícita
|
N2235 | |
| un flujo de control que pasa a través de una declaración de una variable de bloque con duración de almacenamiento estática o de hilo storage duration que no es usable in constant expressions | (since C++23) | P2242R3 |
|
Esta sección está incompleta
Razón: Transferir los contenidos de la lista ordenada en HTML sin formato de abajo a la tabla wiki de arriba, mientras se añaden los documentos/problemas CWG que introdujeron el elemento correspondiente al estándar. Los mini-ejemplos no se conservan, pueden combinarse para formar un ejemplo grande al final de esta página. |
-
a function call expression that calls a function (or a constructor) that is not declared
constexpr
constexpr int n = std::numeric_limits<int>::max(); // OK: max() es constexpr constexpr int m = std::time(nullptr); // Error: std::time() no es constexpr
- a function call to a constexpr function which is declared, but not defined
- a function call to a constexpr function/constructor template instantiation where the instantiation fails to satisfy función/constructor constexpr requirements.
- a function call to a constexpr virtual function, invoked on an object whose dynamic type is constexpr-unknown
- an expression that would exceed the implementation-defined limits
-
an expression whose evaluation leads to any form of core language
undefined
o erróneo
(since C++26)
behavior, except for any potential undefined behavior introduced by
atributos estándar
.
constexpr double d1 = 2.0 / 1.0; // OK constexpr double d2 = 2.0 / 0.0; // Error: no definido constexpr int n = std::numeric_limits<int>::max() + 1; // Error: desbordamiento int x, y, z[30]; constexpr auto e1 = &y - &x; // Error: indefinido constexpr auto e2 = &z[20] - &z[3]; // OK constexpr std::bitset<2> a; constexpr bool b = a[2]; // Comportamiento indefinido, pero no especificado si se detecta
- (hasta C++17) a expresión lambda
-
an lvalue-to-rvalue
conversión implícita
unless applied to...
- un glvalue de tipo (posiblemente calificado con cv) std::nullptr_t
-
un glvalue de tipo-literal no volátil que designa un objeto que es
utilizable en expresiones constantes
int main() { const std::size_t tabsize = 50; int tab[tabsize]; // OK: tabsize es una expresión constante // porque tabsize es utilizable en expresiones constantes // porque tiene tipo integral calificado con const, y // su inicializador es un inicializador constante std::size_t n = 50; const std::size_t sz = n; int tab2[sz]; // Error: sz no es una expresión constante // porque sz no es utilizable en expresiones constantes // porque su inicializador no fue un inicializador constante }
- un glvalue de tipo-literal no volátil que se refiere a un objeto no volátil cuyo tiempo de vida comenzó dentro de la evaluación de esta expresión
T*
a menos que el puntero contenga un valor de puntero nulo o apunte a un objeto cuyo tipo sea
similar
a
T
(since C++26)
dynamic_cast
cuyo operando es un glvalue que se refiere a un objeto cuyo tipo dinámico es constexpr-desconocido
(desde C++20)
reinterpret_cast
constexpr int incr(int& n) { return ++n; } constexpr int g(int k) { constexpr int x = incr(k); // Error: incr(k) no es una expresión constante central // porque el tiempo de vida de k // comenzó fuera de la expresión incr(k) return x; } constexpr int h(int k) { int x = incr(k); // OK: x no necesita ser inicializado // con una expresión constante central return x; } constexpr int y = h(1); // OK: inicializa y con el valor 2 // h(1) es una expresión constante central porque // el tiempo de vida de k comienza dentro de la expresión h(1)
typeid
expression applied to a glvalue of polymorphic type
y ese glvalue se refiere a un objeto cuyo tipo dinámico es constexpr-desconocido
(desde C++20)
|
(since C++20) |
|
(since C++26) |
constexpr void check(int i) { if (i < 0) throw i; } constexpr bool is_ok(int i) { try { check(i); } catch (...) { return false; } return true; } constexpr bool always_throw() { throw 12; return true; } static_assert(is_ok(5)); // OK static_assert(!is_ok(-1)); // OK desde C++26 static_assert(always_throw()); // Error: excepción no capturada
goto
dynamic_cast
o
typeid
o una expresión
new
(desde C++26)
que lanzaría una excepción
donde no hay disponible una definición del tipo de excepción
(desde C++26)
void g() { const int n = 0; constexpr int j = *&n; // OK: fuera de una expresión lambda [=] { constexpr int i = n; // OK: 'n' no es odr-used y no se captura aquí. constexpr int j = *&n; // Mal formado: '&n' sería un odr-use de 'n'. }; }
|
nótese que si el odr-use ocurre en una llamada a función de un cierre, no se refiere a this o a una variable envolvente, ya que accede a un miembro de datos del cierre en su lugar // OK: 'v' & 'm' son odr-used pero no ocurren en una expresión constante // dentro de la lambda anidada auto monad = [](auto v){ return [=]{ return v; }; }; auto bind = [](auto m){ return [=](auto fvm){ return fvm(m()); }; }; // OK tener capturas de objetos automáticos creados durante la evaluación de expresión constante. static_assert(bind(monad(2))(monad)() == monad(2)()); |
(desde C++17) |
Requisitos adicionales
Aunque una expresión E no evalúe nada mencionado anteriormente, está definido por la implementación si E es una expresión constante del núcleo si evaluar E resultaría en comportamiento indefinido en tiempo de ejecución .
Aunque una expresión E no evalúe nada de lo mencionado anteriormente, no está especificado si E es una core constant expression si evaluar E evaluaría cualquiera de los siguientes:
- Una operación con comportamiento indefinido en la biblioteca estándar .
- Una invocación de la macro va_start .
Para los propósitos de determinar si una expresión es una expresión constante núcleo, la evaluación del cuerpo de una función miembro de
std::
allocator
<
T
>
se ignora si
T
es un tipo literal.
Para los propósitos de determinar si una expresión es una expresión constante central, la evaluación de una llamada a un constructor de copia/desplazamiento trivial u operador de asignación de copia/desplazamiento de una union se considera que copia/desplaza el miembro activo de la union, si existe alguno.
|
Para determinar si una expresión es una expresión constante del núcleo, la evaluación de una expresión de identificador que nombra un structured binding bd tiene la siguiente semántica:
|
(since C++26) |
Durante la evaluación de la expresión como una expresión constante principal, todas las expresiones de identificador y usos de * this que se refieren a un objeto o referencia cuyo tiempo de vida comenzó fuera de la evaluación de la expresión se tratan como referencias a una instancia específica de ese objeto o referencia cuyo tiempo de vida y el de todos los subobjetos (incluyendo todos los miembros de unión) incluye toda la evaluación constante.
- Para tal objeto que no es usable en expresiones constantes (desde C++20) , el tipo dinámico del objeto es constexpr-unknown .
- Para tal referencia que no es usable en expresiones constantes (desde C++20) , la referencia se trata como si se vinculara a un objeto no especificado del tipo referenciado cuyo tiempo de vida y el de todos los subobjetos incluye toda la evaluación constante y cuyo tipo dinámico es constexpr-unknown.
Expresión constante entera
Expresión constante entera es una expresión de tipo entero o de enumeración no delimitada convertida implícitamente a un prvalue, donde la expresión convertida es una expresión constante principal.
Si una expresión de tipo clase se utiliza donde se espera una expresión constante integral, la expresión es convertida implícitamente contextualmente a un tipo integral o de enumeración no acotada.
Expresión constante convertida
Una
expresión constante convertida
de tipo
T
es una expresión
convertida implícitamente
al tipo
T
, donde la expresión convertida es una expresión constante, y la secuencia de conversión implícita contiene únicamente:
-
- constexpr conversiones definidas por el usuario
- conversiones de lvalue a rvalue
- promociones integrales
- no restrictivas conversiones integrales
- promociones de punto flotante
- no restrictivas conversiones de punto flotante
| (desde C++17) |
Y si tiene lugar cualquier enlace de referencia , solo puede ser enlace directo .
Los siguientes contextos requieren una expresión constante convertida:
- la expresión-constante de las case etiquetas
- inicializadores de enumerador cuando el tipo subyacente está fijo
- argumentos de plantilla (hasta C++17) constantes integrales y de enumeración
|
(desde C++14) |
| (desde C++26) |
Una expresión constante convertida contextualmente de tipo bool es una expresión convertida contextualmente a bool , donde la expresión convertida es una expresión constante y la secuencia de conversión contiene únicamente las conversiones mencionadas anteriormente.
Los siguientes contextos requieren una expresión constante convertida contextualmente de tipo bool :
| (hasta C++23) | |
|
(desde C++17)
(hasta C++23) |
|
| (desde C++20) |
Entidades constituyentesLos valores constituyentes de un objeto obj se definen de la siguiente manera:
Las referencias constituyentes de un objeto obj incluyen las siguientes referencias:
Los valores constituyentes y referencias constituyentes de una variable var se definen de la siguiente manera:
Para cualquier referencia constituyente ref de una variable var , si ref está vinculada a un objeto temporal o subobjeto del mismo cuya duración se extiende a la de ref , los valores y referencias constituyentes de ese objeto temporal también son valores y referencias constituyentes de var , recursivamente. Entidades representables en constexprLos objetos con duración de almacenamiento estático son referenciables en constexpr en cualquier punto del programa.
Un objeto
obj
con duración de almacenamiento automático es
referenciable en constexpr
desde un punto
Un objeto o referencia
x
es
representable en constexpr
en un punto
|
(desde C++26) | ||||||||
Entidades con inicialización constante
Utilizable en expresiones constantesUna variable es potencialmente-constante si es una constexpr variable o tiene tipo de referencia o tipo integral o enumeración calificado como const no volátil.
Una variable potencialmente constante inicializada de forma constante
var
es
utilizable en expresiones constantes
en un punto
Expresiones manifiestamente evaluadas en tiempo de compilaciónLas siguientes expresiones (incluyendo conversiones al tipo de destino) son manifestamente evaluadas en tiempo de compilación :
Si una evaluación ocurre en un contexto manifiestamente constante puede detectarse mediante
std::is_constant_evaluated
y
|
(desde C++20) |
Funciones y variables necesarias para evaluación constante
Las siguientes expresiones o conversiones son potencialmente evaluadas como constantes :
- expresiones manifiestamente evaluadas como constantes
- expresiones potencialmente evaluadas
- subexpresiones inmediatas de un inicializador entre llaves (puede ser necesaria evaluación constante para determinar si una conversión es de estrechamiento )
- expresiones de toma de dirección que ocurren dentro de una entidad templatizada (puede ser necesaria evaluación constante para determinar si dicha expresión es dependiente de valor )
- subexpresiones de una de las anteriores que no son subexpresión de un operando no evaluado anidado
Una función es necesaria para evaluación constante si es una función constexpr y nombrada por una expresión que es potencialmente evaluada de forma constante.
Una variable es necesaria para evaluación constante si es una variable constexpr o es de tipo integral calificado const no volátil o de tipo referencia y la expresión de identificador que la denota es potencialmente evaluada de forma constante.
Definición de una función por defecto e instanciación de una function template specialization o variable template specialization (since C++14) se activan si la función o variable (since C++14) es necesaria para evaluación constante.
Subexpresión constante
Una subexpresión constante es una expresión cuya evaluación como subexpresión de una expresión e no impediría que e sea una expresión constante central , donde e no es ninguna de las siguientes expresiones:
| (desde C++20) |
Notas
| Macro de prueba de características | Valor | Std | Característica |
|---|---|---|---|
__cpp_constexpr_in_decltype
|
201711L
|
(C++20)
(DR11) |
Generación de definiciones de funciones y variables cuando se necesitan para evaluación constante |
__cpp_constexpr_dynamic_alloc
|
201907L
|
(C++20) | Operaciones para duración de almacenamiento dinámico en constexpr functions |
__cpp_constexpr
|
202306L
|
(C++26) | constexpr cast desde void * : hacia constexpr type-erasure |
202406L
|
(C++26) | constexpr placement new y new [ ] | |
__cpp_constexpr_exceptions
|
202411L
|
(C++26) | constexpr exceptions: [1] , [2] |
Ejemplo
|
Esta sección está incompleta
Razón: sin ejemplo |
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 94 | C++98 |
las expresiones constantes aritméticas no podían
involucrar variables y miembros de datos estáticos |
ahora pueden |
| CWG 366 | C++98 |
las expresiones que involucran literales de cadena
podrían ser expresiones constantes integrales |
no lo son |
| CWG 457 | C++98 |
las expresiones que involucran variables volatile
podían ser expresiones constantes integrales |
no lo son |
| CWG 1293 | C++11 |
no estaba claro si los literales de cadena
son utilizables en expresiones constantes |
son utilizables |
| CWG 1311 | C++11 | Los valores-gl volatile podían usarse en expresiones constantes | prohibido |
| CWG 1312 | C++11 |
reinterpret_cast
está prohibido en expresiones constantes,
pero la conversión hacia y desde void * podría lograr el mismo efecto |
conversiones prohibidas
desde el tipo cv void * hacia un tipo puntero-a-objeto |
| CWG 1313 | C++11 |
se permitía comportamiento indefinido;
se prohibía toda resta de punteros |
Comportamiento indefinido prohibido; resta de
punteros del mismo arreglo permitida |
| CWG 1405 | C++11 |
para objetos que son utilizables en expresiones constantes,
sus subobjetos mutables también eran utilizables |
no son utilizables |
| CWG 1454 | C++11 |
pasar constantes a través de funciones constexpr
mediante referencias no estaba permitido |
permitido |
| CWG 1455 | C++11 | las expresiones constantes convertidas solo podían ser prvalues | pueden ser lvalues |
| CWG 1456 | C++11 |
una expresión constante de dirección no podía
designar la dirección uno más allá del final de un array |
permitido |
| CWG 1535 | C++11 |
una
typeid
expresión cuyo operando es de un
tipo de clase polimórfica no era una expresión constante del núcleo incluso si no se involucraba ninguna verificación en tiempo de ejecución |
la restricción del operando
se limita a glvalues de tipos de clase polimórficos |
| CWG 1581 | C++11 |
las funciones necesarias para evaluación constante
no se requería que estuvieran definidas o instanciadas |
requerido |
| CWG 1613 | C++11 |
las expresiones constantes principales podían evaluar cualquier
referencia odr-usada dentro de expresiones lambda |
algunas referencias no podían
ser evaluadas |
| CWG 1694 | C++11 |
vincular el valor de un temporal a una referencia de duración de almacenamiento
estática era una expresión constante |
no es una
expresión constante |
| CWG 1872 | C++11 |
las expresiones constantes principales podían invocar
constexpr
instanciaciones de plantillas de función
que no cumplían con los requisitos de función constexpr |
dichas instanciaciones
no pueden ser invocadas |
| CWG 1952 | C++11 |
comportamientos indefinidos de la biblioteca estándar
debían ser diagnosticados |
no especificado si
son diagnosticados |
| CWG 2022 | C++98 |
la determinación de expresión constante podría
depender de si se realiza la elisión de copia |
asumir que la elisión de copia
siempre se realiza |
| CWG 2126 | C++11 |
los temporales con inicialización constante y extensión de vida útil de tipos literales calificados como const
no eran utilizables en expresiones constantes |
utilizable |
| CWG 2129 | C++11 | los literales enteros no eran expresiones constantes | sí lo son |
| CWG 2167 | C++11 |
referencias no miembro locales a una evaluación
hicieron la evaluación no constexpr |
referencias no miembro
permitidas |
| CWG 2278 | C++98 | la resolución de CWG issue 2022 no era implementable |
asumir que la elisión de copia
nunca se realiza |
| CWG 2299 | C++14 |
no estaba claro si las macros en
<cstdarg>
pueden usarse en evaluación constante |
va_arg
prohibido,
va_start
no especificado
|
| CWG 2400 | C++11 |
invocar una función virtual constexpr en un objeto no utilizable
en expresiones constantes y cuya vida útil comenzó fuera de la expresión que contiene la invocación podría ser una expresión constante |
no es una
expresión constante |
| CWG 2490 | C++20 |
Las llamadas a (pseudo) destructores carecían de
restricciones en evaluación constante |
restricción añadida |
| CWG 2552 | C++23 |
al evaluar una expresión constante del núcleo, el flujo de control
no podía pasar a través de una declaración de una variable no de bloque |
puede hacerlo |
| CWG 2558 | C++11 | un valor indeterminado podría ser una expresión constante | no es una expresión constante |
| CWG 2647 | C++20 | las variables de tipos calificados como volátiles podrían ser potencialmente constantes | no lo son |
| CWG 2763 | C++11 |
la violación de
[[
noreturn
]]
no se requería
detectar durante la evaluación constante |
requerido |
| CWG 2851 | C++11 |
las expresiones constantes convertidas no
permitían conversiones de punto flotante |
permitir conversiones de punto flotante
que no sean de estrechamiento |
| CWG 2907 | C++11 |
las expresiones constantes principales no podían aplicar
conversiones de lvalue a rvalue a std::nullptr_t glvalues |
pueden aplicar dichas
conversiones |
| CWG 2909 | C++20 |
una variable sin un inicializador solo podría ser
inicializada constantemente si su inicialización por defecto resulta en alguna inicialización siendo realizada |
solo puede ser inicializada
constantemente si su tipo es const-default-initializable |
| CWG 2924 |
C++11
C++23 |
no estaba especificado si una expresión que viola
las restricciones de
[[
noreturn
]]
(C++11) o
[[
assume
]]
(C++23) es una expresión constante principal
|
es
definido por la implementación |
| P2280R4 | C++11 |
evaluar una expresión que contiene una expresión de identificador
o * this que se refiere a un objeto o referencia cuyo tiempo de vida comenzó fuera de esta evaluación no es una expresión constante |
puede ser una
expresión constante |
Véase también
constexpr
especificador
(C++11)
|
especifica que el valor de una variable o función puede calcularse en tiempo de compilación |
|
(C++11)
(obsoleto en C++17)
(eliminado en C++20)
|
verifica si un tipo es un tipo literal
(plantilla de clase) |
|
Documentación C
para
Expresiones constantes
|
|