Espacios de nombres
Variantes
Acciones

Especificador constexpr (desde C++11)

De cppreference.com
< cpp‎ | language
 
 
Lenguaje C++
Temas generales
Control de flujo
Instrucciones de ejecución condicionales
Instrucciones de iteración (bucles)
Declaraciones de salto
Funciones
Declaración de funciones
Declaración de funciones lambda
Especificador inline
Especificación de excepciones (hasta C++20)
Especificador noexcept (C++11)
Excepciones
Espacios de nombres
Tipos
Especificadores
decltype (C++11)
auto (C++11)
alignas (C++11)
const/volatile
constexpr (C++11)
Especificadores de duración de almacenamiento
Inicialización
Expresiones
Representaciones alternas
Literales
Booleanos - Enteros - De punto flotante
De carácter - De cadena - nullptr (C++11)
Definidos por el usuario (C++11)
Utilidades
Atributos (C++11)
Tipos
Declaración de typedef
Declaración de alias de tipo (C++11)
Conversiones
Conversiones implícitas - Conversiones explícitas
static_cast - dynamic_cast
const_cast - reinterpret_cast
Asignación de memoria
Clases
Propiedades de funciones específicas de la clase
Funciones miembro especiales
Plantillas
Misceláneos
 

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:

  • debe tener una destrucción constante, ya sea que:
  • no es de tipo clase ni array (posiblemente multidimensional) de ese tipo, o
  • es de tipo clase o array (posiblemente multidimensional) de ese tipo, ese tipo clase tiene un destructor constexpr, y para una expresión hipotética e cuyo único efecto es destruir el objeto, e sería una expresión constante central si se considerara que la duración del objeto y sus subobjetos no mutables (pero no sus subobjetos mutables) comienzan dentro de e.
(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)
  • el cuerpo de la función debe de ser ya sea eliminado o por defecto (=default;) o contener solamente lo siguiente:
(hasta C++14)
  • el cuerpo de la función no debe contener:
  • una instrucción goto;
  • una instrucción con una etiqueta aparte de case y default;
(hasta C++20)
  • una definición de una variable de tipo no literal;
  • una definición de una variable de duración de almacenamiento estática o local al hilo.
(Un cuerpo de función que está marcado con =default; o =delete; no contiene nada de lo que se especifica anteriormente)
(desde C++14)

Un constructor constexpr cuyo cuerpo de función no es =delete; debe cumplir los requisitos siguientes:

  • para el constructor de una clase o estructura, cada subobjeto de clase base y cada dato miembro no variante, no estático, debe ser inicializado. Si la clase es una clase similar a una unión, para cada uno de sus miembros de unión anónimos no vacíos, debe inicializarse exactamente un miembro variante;
  • para el constructor de una unión no vacía, se debe inicializar exactamente un dato miembro no estático;
(hasta C++20)
  • cada constructor seleccionado para inicializar datos miembro no estáticos y clases base debe ser un constructor constexpr.

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:

  • cada destructor utilizado para destruir miembros de datos no estáticos y clases base debe ser un destructor constexpr.
(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 true para una expresión constante, puede usarse para comprobar si una invocación particular de una función constexpr toma la rama de expresión constante:

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 constexpr, todavía no se permite lanzar excepciones o ejecutar el ensamblador en una expresión constante.

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

constexpr

[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

[editar] Véase también