Partilhar via


constexpr (C++)

A palavra-chave constexpr foi introduzida em C++11 e aprimorada em C++14. Ela significa expressão constante. Como const, ela pode ser aplicada a variáveis: um erro do compilador é gerado quando qualquer código tenta modificar o valor. Ao contrário de const, constexpr também pode ser aplicado a funções e construtores de classe. constexpr indica que o valor, ou valor retornado, é constante e, sempre que possível, é calculado em tempo de compilação.

Um valor integral constexpr pode ser usado sempre que um inteiro const for necessário, como em argumentos de modelo e declarações de matriz. Quando um valor é calculado em tempo de compilação em vez de em tempo de execução, ele ajuda seu programa a executar mais rapidamente e usar menos memória.

Para limitar a complexidade de computações constantes em tempo de compilação e seus impactos potenciais sobre o tempo de compilação, o padrão C++14 exige que os tipos em expressões constantes sejam tipos literais.

Sintaxe

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

Parâmetros

params
Um ou mais parâmetros, cada um deles deve ser um tipo literal e deve ser uma expressão constante.

Retornar valor

Uma variável ou função constexpr deve retornar um tipo literal.

Variáveis constexpr

A principal differença entre variáveis const e constexpr é que a inicialização de uma variável const pode ser adiada até o tempo de execução. Uma variável constexpr deve ser inicializada em tempo de compilação. Todas as variáveis constexpr são const.

  • Uma variável pode ser declarada com constexpr quando tem um tipo literal e é inicializada. Se a inicialização for formada por um construtor, o construtor deverá ser declarado como constexpr.

  • Uma referência pode ser declarada como constexpr quando ambas as condições são atendidas: o objeto referenciado é inicializado por uma expressão consttante e todas as conversões implícitas invocadas durante a inicialização também são expressões consttantes.

  • Todas as declarações de uma variável constexpr ou função devem ter o 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

Funções constexpr

Uma função constexpr é aquela cujo valor retornado é computável em tempo de compilação quando o código de consumo exige isso. O código de consumo requer o valor retornado em tempo de compilação para inicializar uma variável constexpr ou para fornecer um argumento não de modelo de tipo. Quando seus argumentos são valores constexpr, uma função constexpr produz uma constante em tempo de compilação. Quando chamado com argumentos não constexpr ou quando seu valor não é necessário em tempo de compilação, é gerado um valor em tempo de execução como uma função regular. (Esse comportamento duplo poupa você de escrever versões constexpr e não constexpr da mesma função.)

Uma função ou construtor constexpr é implicitamente inline.

As seguintes regras se aplicam a funções constexpr:

  • Uma função constexpr deve aceitar e retornar apenas tipos literais.

  • Uma função constexpr pode ser recursiva.

  • Before C++20, uma constexpr função não pode ser virtual, e um constructor não pode ser definido como constexpr quando a classe de fechamento tem qualquer classe base virtual. No C++20 e posterior, uma constexpr função pode ser virtual. Visual Studio 2019 versão 16.10 e versões posteriores oferecem suporte a funções virtuais quando você especificaify a constexpr opção de /std:c++20 compilador ou posterior.

  • O corpo pode ser definido como = default ou = delete.

  • O corpo pode não conter instruções goto ou blocos try.

  • Uma especialização explícita de um modelo não constexpr pode ser declarada como constexpr:

  • Uma especialização explícita de um modelo constexpr também não precisa ser constexpr:

As seguintes regras se aplicam a funções constexpr no Visual Studio 2017 e posteriores:

  • Ela pode conter instruções if e switch e todas as instruções de loop, incluindo for, for baseadas em intervalo, while e do-while.

  • Pode conter declarações de variáveis locais, mas a variável deve ser inicializada. Deve ser um tipo literal e não pode ser static ou thread-local. A variável declarada localmente não precisa ser conste pode sofrer mutação.

  • Uma função de membro não staticconstexpr não precisa ser implicitamente 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;
}

Dica

No depurador do Visual Studio, ao depurar um build de depuração não otimizado, você pode determinar se uma função constexpr está sendo avaliada no tempo de compilação colocando um ponto de interrupção dentro dela. Se o ponto de interrupção for atingido, a função foi chamada em tempo de execução. Caso contrário, a função foi chamada em tempo de compilação.

extern constexpr

A opção do compilador /Zc:externConstexpr faz com que o compilador aplique o vínculo externo a variáveis declaradas usando extern constexpr. Em versões anteriores do Visual Studio, por padrão ou quando /Zc:externConstexpr- é especificado, o Visual Studio aplica o vínculo interno a variáveis constexpr mesmo quando a palavra-chave extern é usada. A opção /Zc:externConstexpr está disponível no Visual Studio 2017 atualização 15.6 em diante e está desativada por padrão. A opção /permissive- não habilita /Zc:externConstexpr.

Exemplo

O exemplo a seguir mostra variáveis constexpr, funções e um tipo definido pelo usuário. Na última instrução em main(), a função de membro constexprGetValue() é uma chamada em tempo de execução porque o valor não precisa ser conhecido em tempo de compilação.

// 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 ou posterior.

Confira também

Declarações e definições
const