constexpr
specifier
(since C++11)
-
-
constexpr- especifica que el valor de una variable , structured binding (since C++26) o función puede aparecer en constant expressions
-
Contenidos |
Explicación
El constexpr especificador declara que es posible evaluar el valor de las entidades en tiempo de compilación. Dichas entidades pueden entonces utilizarse donde solo se permiten expresiones constantes de tiempo de compilación (siempre que se proporcionen los argumentos de función apropiados).
Un constexpr especificador utilizado en una declaración de objeto o función miembro no estática (hasta C++14) implica const .
Un constexpr especificador utilizado en la primera declaración de una función o static miembro de datos (desde C++17) implica inline . Si cualquier declaración de una función o plantilla de función tiene un constexpr especificador, entonces cada declaración debe contener ese especificador.
constexpr variable
Una variable o plantilla de variable (desde C++14) puede declararse constexpr si se cumplen todas las siguientes condiciones:
- La declaración es una definición .
- Es de un literal type .
- Está inicializada (por la declaración).
|
(hasta C++26) |
| (desde C++26) |
|
Si una variable constexpr no es local a la unidad de traducción , no debe inicializarse para referirse a una entidad local a la unidad de traducción que sea utilizable en expresiones constantes, ni tener un subobjeto que se refiera a dicha entidad. Dicha inicialización no está permitida en una unidad de interfaz de módulo (fuera de su fragmento de módulo privado , si existe) o en una partición de módulo, y está obsoleta en cualquier otro contexto. |
(desde C++20) |
constexpr función
Una función o plantilla de función puede declararse constexpr .
Una función es constexpr-suitable si se satisfacen todas las siguientes condiciones:
|
(hasta C++20) |
|
(hasta C++23) |
|
(desde C++20) |
|
(hasta C++14) | ||
|
(desde C++14)
(hasta C++23) |
Excepto las funciones constexpr instanciadas, las funciones no template constexpr deben ser constexpr-suitable.
|
Para una función constexpr que no es un constructor, que no está predeterminada ni es una plantilla, si no existen valores de argumentos tales que una invocación de la función pueda ser una subexpresión evaluada de una expresión constante principal , el programa está mal formado, no se requiere diagnóstico. Para una función de plantilla constexpr , si ninguna especialización de la función/plantilla de clase haría que la función de plantilla sea adecuada para constexpr cuando se considera como una función no plantilla, el programa está mal formado, no se requiere diagnóstico. |
(until C++23) |
Una invocación de una función constexpr en un contexto dado produce el mismo resultado que una invocación de una función no constexpr equivalente en el mismo contexto en todos los aspectos, con las siguientes excepciones:
- Una invocación de una función constexpr puede aparecer en una expresión constante .
- Copy elision no se realiza en una expresión constante.
constexpr constructor
Además de los requisitos de las funciones constexpr , un constructor también debe cumplir todas las siguientes condiciones para ser apto para constexpr:
|
(hasta C++23) |
- La clase no tiene ninguna virtual base class .
|
Para un constructor constexpr que no es ni predeterminado ni templado, si no existen valores de argumentos tales que una invocación de la función pueda ser una subexpresión evaluada de la expresión completa de inicialización de algún objeto sujeto a expresión constante , el programa está mal formado, no se requiere diagnóstico. |
(until C++23) |
constexpr destructor
|
Los destructores no pueden ser constexpr , pero un destructor trivial puede ser invocado implícitamente en expresiones constantes. |
(hasta C++20) | ||
|
Además de los requisitos de las funciones constexpr , un destructor también debe cumplir todas las siguientes condiciones para ser apto como constexpr:
|
(desde C++20) |
Notas
|
Debido a que el operador
constexpr int f(); constexpr bool b1 = noexcept(f()); // false, undefined constexpr function constexpr int f() { return 0; } constexpr bool b2 = noexcept(f()); // true, f() is a constant expression |
(hasta C++17) |
|
Es posible escribir una función constexpr cuya invocación nunca pueda satisfacer los requisitos de una expresión constante del núcleo: void f(int& i) // not a constexpr function { i = 0; } constexpr void g(int& i) // well-formed since C++23 { f(i); // unconditionally calls f, cannot be a constant expression } |
(since C++23) |
Los constructores constexpr están permitidos para clases que no son tipos literales. Por ejemplo, el constructor por defecto de std::shared_ptr es constexpr, permitiendo inicialización constante .
Las variables de referencia pueden declararse constexpr (sus inicializadores deben ser expresiones constantes de referencia ):
static constexpr int const& x = 42; // referencia constexpr a un objeto const int // (el objeto tiene duración de almacenamiento estático // debido a la extensión de vida por una referencia estática)
|
Aunque try blocks y el ensamblado en línea están permitidos en funciones constexpr, lanzar excepciones que no son capturadas (desde C++26) o ejecutar el ensamblado sigue estando prohibido en una expresión constante. Si una variable tiene destrucción constante, no es necesario generar código máquina para invocar su destructor, incluso si su destructor no es trivial. Una función constexpr que no es lambda, no es miembro especial y no está templatizada no puede convertirse implícitamente en una función inmediata. Los usuarios deben marcarla explícitamente como consteval para que dicha definición de función sea válida. |
(desde C++20) |
| Macro de prueba de características | Valor | Estándar | Característica |
|---|---|---|---|
__cpp_constexpr
|
200704L
|
(C++11) | constexpr |
201304L
|
(C++14) | constexpr relajado , métodos constexpr no const | |
201603L
|
(C++17) | Lambda constexpr | |
201907L
|
(C++20) | Inicialización por defecto trivial default initialization y declaración asm en funciones constexpr | |
202002L
|
(C++20) | Cambiar el miembro activo de una unión en evaluación constante | |
202110L
|
(C++23) | Variables no literales , etiquetas y sentencias goto en funciones constexpr | |
202207L
|
(C++23) | Relajación de algunas restricciones constexpr | |
202211L
|
(C++23) | Permitir variables static constexpr en funciones constexpr | |
202306L
|
(C++26) | Conversión constexpr desde void * : hacia type-erasure constexpr | |
__cpp_constexpr_in_decltype
|
201711L
|
(C++11)
(DR) |
Generación de definiciones de funciones y variables cuando son necesarias para evaluación constante |
__cpp_constexpr_dynamic_alloc
|
201907L
|
(C++20) | Operaciones para duración de almacenamiento dinámico en funciones constexpr |
Palabras clave
Ejemplo
Define funciones constexpr de C++11/14 que calculan factoriales; define un tipo literal que extiende literales de cadena:
#include <iostream> #include <stdexcept> // Las funciones constexpr de C++11 usan recursión en lugar de iteración constexpr int factorial(int n) { return n <= 1 ? 1 : (n * factorial(n - 1)); } // Las funciones constexpr de C++14 pueden usar variables locales y bucles #if __cplusplus >= 201402L constexpr int factorial_cxx14(int n) { int res = 1; while (n > 1) res *= n--; return res; } #endif // C++14 // Una clase literal class conststr { const char* p; std::size_t sz; public: template<std::size_t N> constexpr conststr(const char(&a)[N]): p(a), sz(N - 1) {} // Las funciones constexpr señalan errores lanzando excepciones // en C++11, deben hacerlo desde el operador condicional ?: constexpr char operator[](std::size_t n) const { return n < sz ? p[n] : throw std::out_of_range(""); } constexpr std::size_t size() const { return sz; } }; // Las funciones constexpr de C++11 tenían que poner todo en una única sentencia return // (C++14 no tiene ese requisito) constexpr std::size_t countlower(conststr s, std::size_t n = 0, std::size_t c = 0) { return n == s.size() ? c : 'a' <= s[n] && s[n] <= 'z' ? countlower(s, n + 1, c + 1) : countlower(s, n + 1, c); } // Una función de salida que requiere una constante en tiempo de compilación, para pruebas template<int n> struct constN { constN() { std::cout << n << '\n'; } }; int main() { std::cout << "4! = "; constN<factorial(4)> out1; // calculado en tiempo de compilación volatile int k = 8; // deshabilitar optimización usando volatile std::cout << k << "! = " << factorial(k) << '\n'; // calculado en tiempo de ejecución std::cout << "El número de letras minúsculas en \"Hello, world!\" es "; constN<countlower("Hello, world!")> out2; // convertido implícitamente a conststr constexpr int a[12] = {0, 1, 2, 3, 4, 5, 6, 7, 8}; constexpr int length_a = sizeof a / sizeof(int); // std::size(a) en C++17, // std::ssize(a) en C++20 std::cout << "Array de longitud " << length_a << " tiene elementos: "; for (int i = 0; i < length_a; ++i) std::cout << a[i] << ' '; std::cout << '\n'; }
Salida:
4! = 24 8! = 40320 El número de letras minúsculas en "Hello, world!" es 9 Array de longitud 12 tiene elementos: 0 1 2 3 4 5 6 7 8 0 0 0
Informes de defectos
Los siguientes informes de defectos que modifican el comportamiento se aplicaron retroactivamente a los estándares de C++ publicados anteriormente.
| DR | Aplicado a | Comportamiento publicado | Comportamiento correcto |
|---|---|---|---|
| CWG 1358 | C++11 |
las funciones
constexpr
con plantilla también necesitaban
tener al menos un valor de argumento válido |
no es necesario |
| CWG 1359 | C++11 |
los constructores de unión
constexpr
deben inicializar todos los miembros de datos |
inicializa exactamente un miembro
de datos para uniones no vacías |
| CWG 1366 | C++11 |
las clases con constructores
constexpr
cuyos cuerpos de función
son = default o = delete podrían tener clases base virtuales |
tales clases no pueden
tener clases base virtuales |
| CWG 1595 | C++11 |
los constructores delegados
constexpr
requerían
que todos los constructores involucrados fueran constexpr |
solo requiere que el constructor
objetivo sea constexpr |
| CWG 1712 | C++14 |
se requería que una plantilla de variable
constexpr
tuviera todas sus declaraciones conteniendo el especificador constexpr [1] |
ya no es requerido |
| CWG 1911 | C++11 | los constructores constexpr para tipos no literales no estaban permitidos | permitidos en inicialización constante |
| CWG 2004 | C++11 |
la copia/movimiento de una unión con un miembro mutable
estaba permitida en una expresión constante |
las variantes mutables descalifican
la copia/movimiento implícita |
| CWG 2022 | C++98 |
si las funciones equivalentes
constexpr
y no-
constexpr
producen resultados iguales podría depender de si se realiza la elisión de copia |
asumir que la elisión de copia siempre
se realiza en expresiones constantes |
| CWG 2163 | C++14 |
las etiquetas estaban permitidas en funciones
constexpr
aunque las sentencias goto estén prohibidas |
las etiquetas también prohibidas |
| CWG 2268 | C++11 |
la copia/movimiento de una unión con un miembro mutable
estaba prohibida por la resolución de CWG issue 2004 |
permitida si el objeto es creado
dentro de la expresión constante |
| CWG 2278 | C++98 | la resolución de CWG issue 2022 no era implementable |
asumir que la elisión de copia nunca
se realiza en expresiones constantes |
| CWG 2531 | C++11 |
una variable no inline se volvía inline
si se redeclaraba con constexpr |
la variable no
se vuelve inline |
- ↑ Es redundante porque no puede haber más de una declaración de una variable template con el constexpr especificador.
Véase también
| expresión constante | define una expresión que puede evaluarse en tiempo de compilación |
consteval
especificador
(C++20)
|
especifica que una función es una función inmediata , es decir, cada llamada a la función debe estar en una evaluación constante |
constinit
especificador
(C++20)
|
afirma que una variable tiene inicialización estática, es decir inicialización a cero e inicialización constante |
|
Documentación de C
para
constexpr
|
|