Especificador constexpr
(desde C++11)
constexpr
- Especifica que el valor de una variable o función puede aparecer en expresiones constantes.
Contenido |
[editar] Explicación
El especificador constexpr
declara que es posible evaluar el valor de la función o variable en tiempo de compilación. Tales variables y funciones pueden entonces utilizarse pueden donde solamente se permiten expresiones constantes en tiempo de compilación (dado que se suministren los argumentos de función apropiados). Un especificador constexpr utilizado en una declaración de objeto o función miembro no estática (hasta C++14) implica const. Un especificador constexpr utilizado en una función o la declaración de una variable miembro estática (desde C++17) implica inline. Si alguna declaración de una función o plantilla de función tiene un especificador constexpr
, cada declaración debe contener ese especificador.
Una variable constexpr debe cumplir los siguientes requisitos:
- su tipo debe de ser un LiteralType;
- debe inicializarse inmediatemente;
- la expresión completa de su inicialización, incluyendo todas las conversiones implícitas, llamadas a constructores, etc., deben ser una expresión constante;
|
(desde C++20) |
Si una variable constexpr no es local a la unidad de traducción, no deberá inicializarse para que apunte a, o se refiera a, o tenga un subobjeto (posiblemente recursivo) que apunte a, o se refiera a, una entidad local a la unidad de traducción que sea utilizable en expresiones constantes. Tal inicialización no se permite en una unidad de interfaz de módulo (fuera de su fragmento de módulo privado, si es que lo tiene) o una partición de módulo, y está en desuso en cualquier otro contexto. |
(desde C++20) |
Una función constexpr debe cumplir los siguientes requisitos:
|
(hasta C++20) |
|
(desde C++20) |
- su tipo de retorno (si lo tiene) debe ser un LiteralType;
- cada uno de sus parámetros deben ser un LiteralType
- para el constructor y destructor (desde C++20), la clase no debe tener clases base virtuales;
- existe al menos un conjunto de valores de argumentos tal que la invocación de la función podría ser una subexpresión evaluada de un expresión constante central (para los constructores, el uso en un inicializador constante es suficiente) (desde C++14). No se requiere diagnóstico de una violación de esta viñeta;
|
(hasta C++20) |
|
(hasta C++14) | ||
|
(desde C++14) |
Un constructor constexpr cuyo cuerpo de función no es =delete; debe cumplir los requisitos siguientes:
|
(hasta C++20) |
- cada constructor seleccionado para inicializar datos miembro no estáticos y clases base debe ser un constructor
constexpr
.
- cada constructor seleccionado para inicializar datos miembro no estáticos y clases base debe ser un constructor
Los destructores no pueden ser constexpr, pero se puede llamar a un destructor trivial en expresiones constantes. |
(hasta C++20) |
Un destructor constexpr cuyo cuerpo de función no es = delete; debe cumplir los siguientes requisitos adicionales:
|
(desde C++20) |
Para las plantillas de función constexpr
y las funciones miembro constexpr
de las plantillas de clase, al menos una especialización debe cumplir los requisitos mencionados anteriormente. Otras especializaciones todavía se consideran constexpr
, aunque una llamada a dicha función no pueda aparecer en una expresión constante.
[editar] Notas
Como el operador noexcept siempre devuelve constexpr int f(); constexpr bool b1 = noexcept(f()); // falso, función constexpr indefinida constexpr int f() { return 0; } constexpr bool b2 = noexcept(f()); // verdadero, f() es una expresión constante |
(hasta C++17) |
Se permiten los constructores constexpr
para clases que no son tipos literal. Por ejemplo, el constructor por defecto de std::unique_ptr es constexpr
, permitiendo la inicialización por constante.
Las variables referencia pueden declararse constexpr
(sus inicializadores tiene que ser expresiones de referencia constante ):
static constexpr int const& x = 42; // referencia constexpr a un objeto const int // (el objeto tiene duración de almacenamiento estática // debido a la extensión de su duración mediante // una referencia estática)
Aun cuando los bloques try y el ensamblador en línea se permiten en funciones Si una variable tiene una destrucción constante, no se necesita generar código de máquina para poder llamar a su destructor, incluso si su destructor no es trivial |
(desde C++20) |
[editar] Palabras clave
[editar] Ejemplo
Definición de una función constexpr
en C++11 que calcula factoriales, y un tipo literal que extiende literales de cadena
#include <iostream> #include <stdexcept> // las funciones constexpr en C++11 usan recursión en lugar de iteración // (las funciones constexpr en C++14 pueden usar variables locales y bucles) constexpr int factorial(int n) { return n <= 1 ? 1 : (n * factorial(n - 1)); } // 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) {} // en C++11, las funciones constexpr señalan los errores lanzando // excepciones, deben hacerlo desde el operador condicional ?: constexpr char operator[](std::size_t n) { return n < sz ? p[n] : throw std::out_of_range(""); } constexpr std::size_t size() { return sz; } }; // las funciones constexpr en C++11 tenían que poner todo en una sola instrucción 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 : s[n] >= 'a' && s[n] <= 'z' ? countlower(s, n+1, c+1) : countlower(s, n+1, c); } // función de salida que requiere una constante en tiempo de compilación para prueba 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 utilizando volatile std::cout << k << "! = " << factorial(k) << '\n'; // calculado en tiempo de ejecución std::cout << "Numero de letras minusculas en \"Hola, mundo!\" es "; constN<countlower("Hola, mundo!")> out2; // convertido implícitamente a conststr }
Salida:
4! = 24 8! = 40320 Numero de letras minusculas en "Hola, mundo!" es 8
[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 1911 | C++14 | Constructores constexpr para tipos no de literal no se permitían | Se permiten en inicialización constante. |
CWG 2004 | C++14 | Copia/movimiento de una unión con un miembro mutable se permitía en una expresión constante |
Variantes mutables descalifican la copia/movimiento implícita(o) |
CWG 2163 | C++14 | Se permitían etiquetas en funciones constexpr aun cuando gotos están prohibidos |
Las etiquetas también se prohiben |
CWG 2268 | C++14 | Copia/movimiento de una unión con un miembro mutable estaba prohibido por cwg 2004 |
Se permite si el objeto se creó dentro de una expresión constante |