Namespaces
Variants

constexpr specifier (since C++11)

From cppreference.net
C++ language
General topics
Flow control
Conditional execution statements
Iteration statements (loops)
Jump statements
Functions
Function declaration
Lambda function expression
inline specifier
Dynamic exception specifications ( until C++17* )
noexcept specifier (C++11)
Exceptions
Namespaces
Types
Specifiers
constexpr (C++11)
consteval (C++20)
constinit (C++20)
Storage duration specifiers
Initialization
Expressions
Alternative representations
Literals
Boolean - Integer - Floating-point
Character - String - nullptr (C++11)
User-defined (C++11)
Utilities
Attributes (C++11)
Types
typedef declaration
Type alias declaration (C++11)
Casts
Memory allocation
Classes
Class-specific function properties
Special member functions
Templates
Miscellaneous

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:

(hasta C++26)
(desde C++26)

  • Tiene destrucción constante, lo que significa que debe satisfacerse una de las siguientes condiciones:
  • No es de tipo clase ni un arreglo (posiblemente multidimensional) del mismo.
  • Es de un tipo clase con un destructor constexpr o un arreglo (posiblemente multidimensional) del mismo, y para una expresión hipotética e cuyo único efecto es destruir el objeto, e sería una expresión constante central si el tiempo de vida del objeto y sus subobjetos no mutables (pero no sus subobjetos mutables) se considerara que comienza dentro de e .

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)
  • Su tipo de retorno (si existe) es un literal type .
  • Cada uno de sus tipos de parámetros es un literal type.
(hasta C++23)
(desde C++20)
  • Su cuerpo de función es = default , = delete , o una sentencia compuesta que contiene únicamente lo siguiente:
(hasta C++14)
  • Su cuerpo de función es = default , = delete , o una sentencia compuesta que (hasta C++20) no contiene lo siguiente:
(hasta C++20)
(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:

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:

  • Su cuerpo de función es = delete o satisface los siguientes requisitos adicionales:
  • Si la clase es una union con miembros variantes, exactamente uno de ellos está inicializado.
  • Si la clase es una union-like class , pero no es una union, para cada uno de sus miembros union anónimos que tienen miembros variantes, exactamente uno de ellos está inicializado.
  • Cada miembro de datos no estático no variante y cada subobjeto de clase base está inicializado.
(hasta C++20)
  • Si el constructor es un delegating constructor , el constructor objetivo es un constructor constexpr .
  • Si el constructor es un constructor no delegante, cada constructor seleccionado para inicializar miembros de datos no estáticos y subobjetos de clase base es un constructor constexpr .
(hasta C++23)

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:

  • Para cada subobjeto de tipo clase o array (posiblemente multidimensional) del mismo, ese tipo de clase debe tener un destructor constexpr .
(hasta C++23)
  • La clase no tiene ninguna clase base virtual.
(desde C++20)

Notas

Debido a que el operador noexcept siempre retorna true para una expresión constante, puede utilizarse para verificar 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()); // 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

constexpr

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
  1. 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