constexpr (C++)

La palabra clave constexpr se introdujo en C++11 y se mejoró en C++14. Significa Expresión constant. Al igual que const, se puede aplicar a las variables: se produce un error del compilador si algún código intenta modificar el valor. A diferencia de const, constexpr también se puede aplicar a los constructors de clase y funciones. constexpr indica que el valor o el valor devuelto, es constant y, si es posible, se procesará 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 constant en tiempo de compilación y su posible impacto en este tiempo, el estándar de C++14 requiere los tipos en expresiones constant para ser tipos literales.

Sintaxis

constexprliteral-typeidentifier=constant-expression;
constexprliteral-typeidentifier{constant-expression};
constexprliteral-typeidentifier(params);
constexprctor(params);

Parámetros

params
Uno o más parámetros, los cuales deben ser cada uno un tipo literal y deben ser en sí mismos una expresión constant.

Valor devuelto

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

Variables constexpr

La 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. Si la inicialización es realizada for por medio de un constructor, el constructor debe declararse como constexpr.

  • Se puede declarar una referencia como constexpr cuando se cumplen estas condiciones: el objeto al que se hace referencia se inicializa mediante una expresión constant y las conversiones implícitas invocadas durante la inicialización también son expresiones constant.

  • Todas las declaraciones de una función o variable constexpr 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 constexprgenera una constant 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.

  • Seafore C++20, una constexpr función no puede ser virtual y no se puede definir un constructor como constexpr cuando la clase envolvente tiene ninguna clase base virtual. En C++20 y versiones posteriores, una constexpr función puede ser virtual. Visual Studio 2019, versión 16.10 y versiones posteriores, admiten constexpr funciones virtuales cuando se especificaify la /std:c++20 opción del compilador 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. Si se alcanza el punto de interrupción, significa que se llamó a la función en tiempo de ejecución. En caso contrario, 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 /Zc:externConstexpr- especifica, Visual Studio aplica la vinculación interna a las variables constexpr incluso cuando se usa 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 constexprGetValue() 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