Compartir a través de


constexpr (C++)

La palabra clave constexpr se introdujo en C++11 y se mejoró en C++14. Significa expresión constante. Igual que const, se puede aplicar a variables: se genera un error del compilador cuando cualquier código intenta modificar el valor. A diferencia de const, constexpr también se puede aplicar a funciones y constructores de clases. constexpr Indica que el valor, o valor devuelto, es constante y, cuando es posible, se calcula en tiempo de compilación.

Un valor entero constexpr se puede usar donde se requiera un entero const, como es el caso de las declaraciones de matrices y los argumentos de plantillas. Y si un valor se procesa en tiempo de compilación en lugar de en tiempo de ejecución, ayuda a su programa a ejecutarse más rápidamente y a usar menos memoria.

Para limitar la complejidad de los cálculos de constantes en tiempo de compilación y sus posibles impactos en el tiempo de compilación, el estándar C++14 requiere que los tipos de las expresiones constantes sean tipos literales.

Sintaxis

constexpr literal-type identifier = constant-expression ;
constexpr literal-type identifier { constant-expression } ;
constexpr literal-type identifier ( params ) ;
constexpr ctor ( params ) ;

Parámetros

params
Uno o más parámetros, cada uno de los cuales debe ser un tipo literal y debe ser a su vez una expresión constante.

Valor devuelto

Una variable o función constexpr debe devolver un tipo literal.

Variables constexpr

La principal diferencia entre las variables const y constexpr es que la inicialización de una variable const se puede aplazar hasta el tiempo de ejecución. Una variable constexpr se debe inicializar en tiempo de compilación. Todas las variables constexpr son const.

  • Una variable se puede declarar con constexpr, si tiene un tipo literal y está inicializada. If la inicialización la realiza un constructor, el constructor debe declararse como constexpr.

  • Una referencia puede declararse como constexpr si se cumplen estas dos condiciones: el objeto al que hace referencia se inicializa mediante una expresión constante y todas las conversiones implícitas que se invoquen durante la inicialización son también expresiones constantes.

  • Todas las declaraciones de una variable constexpr o función deben tener el especificador constexpr.

constexpr float x = 42.0;
constexpr float y{108};
constexpr float z = exp(5, 3);
constexpr int i; // Error! Not initialized
int j = 0;
constexpr int k = j + 1; //Error! j not a constant expression

funcionesconstexpr

Una función constexpr es aquella cuyo valor devuelto se calcula durante el tiempo de compilación cuando el código usado lo requiere. El código de consumo requiere el valor de retorno en tiempo de compilación para inicializar una variable constexpr o proporcionar un argumento de plantilla que no sea de tipo. Cuando sus argumentos son valores constexpr, una función constexpr genera una constante en tiempo de compilación. Cuando se llama con argumentos que no sean constexpr o cuando su valor no se requiere en tiempo de compilación, genera un valor en tiempo de ejecución como una función normal. (Este doble comportamiento le evita tener que escribir tanto versiones constexpr como las que no sean constexpr de la misma función).

Una función constexpr o constructor es implícitamente inline.

Las reglas siguientes se aplican a las funciones constexpr:

  • Una función constexpr debe aceptar y devolver únicamente tipos literales.

  • Una función constexpr puede ser recursiva.

  • Antes de C++20, una función constexpr no podía ser virtual y un constructor no se podía definir como constexpr cuando la clase envolvente tenía clases de base virtual. En C++20 y versiones posteriores, una función constexpr puede ser virtual. Visual Studio 2019, versión 16.10 y versiones posteriores admiten funciones virtuales constexpr cuando se especifica la opción del compilador /std:c++20 o posterior.

  • El cuerpo se puede definir como = default o = delete.

  • El cuerpo no puede contener instrucciones goto ni bloques try.

  • Una especialización explícita de una plantilla que no sea constexpr no se puede declarar como constexpr:

  • Una especialización explícita de una plantilla constexpr no tiene que ser también constexpr:

Las reglas siguientes se aplican a las funciones constexpr de Visual Studio 2017 y versiones posteriores:

  • Puede contener instrucciones if y switch, y todas las instrucciones de bucle, incluidas for, basadas en intervalos for, while y do-while.

  • Puede contener declaraciones de variables locales, pero la variable debe inicializarse. Debe ser un tipo literal y no puede ser static ni local para subprocesos. La variable declarada localmente no es necesaria para ser const y puede mutar.

  • No es necesario que una función constexpr que no sea miembro static sea implícitamente const.

constexpr float exp(float x, int n)
{
    return n == 0 ? 1 :
        n % 2 == 0 ? exp(x * x, n / 2) :
        exp(x * x, (n - 1) / 2) * x;
}

Sugerencia

En el depurador de Visual Studio, al depurar una compilación de depuración no optimizada, puede indicar si una función constexpr se evalúa en tiempo de compilación colocando un punto de interrupción en su interior. If si se alcanza el punto de interrupción, significa que se llamó a la función en tiempo de ejecución. If no, significa que se llamó a la función en tiempo de compilación.

constexpr externo

La opción del compilador /Zc:externConstexpr hace que el compilador aplique la vinculación externa a las variables declaradas mediante constexpr externo. En versiones anteriores de Visual Studio, ya sea de forma predeterminada o cuando se especificaba /Zc:externConstexpr-, Visual Studio aplicaba la vinculación interna a las variables constexpr incluso cuando se usaba la palabra clave extern. La opción /Zc:externConstexpr está disponible a partir de la actualización 15.6 de Visual Studio 2017 y está desactivada de forma predeterminada. La opción /permissive- no habilita /Zc:externConstexpr.

Ejemplo

En el siguiente ejemplo se muestran las variables constexpr, las funciones y un tipo definido por usuario. En la última instrucción en main(), la función miembro constexpr GetValue() es una llamada en tiempo de ejecución porque no es necesario conocer el valor en tiempo de compilación.

// constexpr.cpp
// Compile with: cl /EHsc /W4 constexpr.cpp
#include <iostream>

using namespace std;

// Pass by value
constexpr float exp(float x, int n)
{
    return n == 0 ? 1 :
        n % 2 == 0 ? exp(x * x, n / 2) :
        exp(x * x, (n - 1) / 2) * x;
}

// Pass by reference
constexpr float exp2(const float& x, const int& n)
{
    return n == 0 ? 1 :
        n % 2 == 0 ? exp2(x * x, n / 2) :
        exp2(x * x, (n - 1) / 2) * x;
}

// Compile-time computation of array length
template<typename T, int N>
constexpr int length(const T(&)[N])
{
    return N;
}

// Recursive constexpr function
constexpr int fac(int n)
{
    return n == 1 ? 1 : n * fac(n - 1);
}

// User-defined type
class Foo
{
public:
    constexpr explicit Foo(int i) : _i(i) {}
    constexpr int GetValue() const
    {
        return _i;
    }
private:
    int _i;
};

int main()
{
    // foo is const:
    constexpr Foo foo(5);
    // foo = Foo(6); //Error!

    // Compile time:
    constexpr float x = exp(5, 3);
    constexpr float y { exp(2, 5) };
    constexpr int val = foo.GetValue();
    constexpr int f5 = fac(5);
    const int nums[] { 1, 2, 3, 4 };
    const int nums2[length(nums) * 2] { 1, 2, 3, 4, 5, 6, 7, 8 };

    // Run time:
    cout << "The value of foo is " << foo.GetValue() << endl;
}

Requisitos

Visual Studio 2015 o posterior.

Consulte también

Declaraciones y definiciones
const