Diferença entre `constexpr` e` const`

593

Qual é a diferença entre constexpre const?

  • Quando posso usar apenas um deles?
  • Quando posso usar os dois e como devo escolher um?
MBZ
fonte
71
constexprcria uma constante em tempo de compilação; constsignifica simplesmente que o valor não pode ser alterado.
0x499602D2
Talvez este artigo da boost/hanabiblioteca possa esclarecer alguns constexprproblemas nos quais você pode usar constexpre onde não pode: boost.org/doc/libs/1_69_0/libs/hana/doc/html/…
Andry
@ 0x499602D2 " significa simplesmente que o valor não pode ser alterado " Para um escalar inicializado com um literal, um valor que não pode ser alterado também é uma constante de tempo de compilação.
curiousguy
@curiousguy Sim, meu comentário foi muito simplificado. É certo que eu era novo na constexprépoca também :)
0x499602D2 20/01

Respostas:

587

Sintaxe e significado básico

Ambas as palavras-chave podem ser usadas na declaração de objetos e também de funções. A diferença básica quando aplicada a objetos é esta:

  • constdeclara um objeto como constante . Isso implica uma garantia de que, uma vez inicializado, o valor desse objeto não será alterado, e o compilador poderá usar esse fato para otimizações. Também ajuda a impedir que o programador escreva código que modifica objetos que não deveriam ser modificados após a inicialização.

  • constexprdeclara um objeto como apto para uso no que o Padrão chama de expressões constantes . Mas observe que constexprnão é a única maneira de fazer isso.

Quando aplicada a funções, a diferença básica é esta:

  • constsó pode ser usado para funções membro não estáticas, não para funções em geral. Isso garante que a função de membro não modifique nenhum dos membros de dados não estáticos.

  • constexprpode ser usado com funções membro e não membro, além de construtores. Ele declara a função adequada para uso em expressões constantes . O compilador o aceitará apenas se a função atender a certos critérios (7.1.5 / 3,4), o mais importante (†) :

    • O corpo da função deve ser não virtual e extremamente simples: além de typedefs e afirmações estáticas, apenas uma única returninstrução é permitida. No caso de um construtor, apenas uma lista de inicialização, typedefs e declaração estática são permitidos. ( = defaulte também = deletesão permitidos.)
    • A partir do C ++ 14, as regras são mais flexíveis, o que é permitido desde então dentro de uma função constexpr: asmdeclaração, uma gotoinstrução, uma instrução com um rótulo diferente de casee default, try-block, a definição de uma variável não literal. tipo, definição de uma variável de duração de armazenamento estático ou de encadeamento, a definição de uma variável para a qual nenhuma inicialização é executada.
    • Os argumentos e o tipo de retorno devem ser tipos literais (por exemplo, tipos muito simples, geralmente escalares ou agregados)

Expressões constantes

Como dito acima, constexprdeclara os dois objetos e as funções como adequados para uso em expressões constantes. Uma expressão constante é mais do que meramente constante:

  • Pode ser usado em locais que exigem avaliação em tempo de compilação, por exemplo, parâmetros de modelo e especificadores de tamanho de matriz:

    template<int N>
    class fixed_size_list
    { /*...*/ };
    
    fixed_size_list<X> mylist;  // X must be an integer constant expression
    
    int numbers[X];  // X must be an integer constant expression
  • Mas observe:

    • Declarar algo como constexprnão garante necessariamente que será avaliado em tempo de compilação. Ele pode ser usado para tal, mas ele pode ser usado em outros lugares que são avaliadas em tempo de execução, bem.

    • Um objeto pode ser adequado para uso em expressões constantes sem ser declarado constexpr. Exemplo:

      int main()
      {
        const int N = 3;
        int numbers[N] = {1, 2, 3};  // N is constant expression
      }

    Isso é possível porque N, sendo constante e inicializado no momento da declaração com um literal, satisfaz os critérios para uma expressão constante, mesmo que não seja declarada constexpr.

Então, quando eu realmente tenho que usar constexpr?

  • Um objeto como o Nacima pode ser usado como expressão constante sem ser declarado constexpr. Isso vale para todos os objetos que são:

    • const
    • do tipo integral ou de enumeração e
    • inicializado no momento da declaração com uma expressão que é uma expressão constante

    [Isso se deve ao §5.19 / 2: Uma expressão constante não deve incluir uma subexpressão que envolva "uma modificação de valor-para-valor, a menos que [...] um valor de glifo do tipo integral ou de enumeração [...]". Obrigado a Richard Smith por corrigir minha afirmação anterior de que isso era verdade para todos os tipos literais.]

  • Para que uma função seja adequada para uso em expressões constantes, ela deve ser declarada explicitamente constexpr; não é suficiente apenas satisfazer os critérios para funções de expressão constante. Exemplo:

    template<int N>
    class list
    { };
    
    constexpr int sqr1(int arg)
    { return arg * arg; }
    
    int sqr2(int arg)
    { return arg * arg; }
    
    int main()
    {
      const int X = 2;
      list<sqr1(X)> mylist1;  // OK: sqr1 is constexpr
      list<sqr2(X)> mylist2;  // wrong: sqr2 is not constexpr
    }

Quando posso / devo usar os dois conste constexpr juntos?

A. Nas declarações de objetos. Isso nunca é necessário quando as duas palavras-chave se referem ao mesmo objeto a ser declarado. constexprimplica const.

constexpr const int N = 5;

é o mesmo que

constexpr int N = 5;

No entanto, observe que pode haver situações em que as palavras-chave se referem a diferentes partes da declaração:

static constexpr int N = 3;

int main()
{
  constexpr const int *NP = &N;
}

Aqui, NPé declarado como um endereço de expressão constante, ou seja, um ponteiro que é ele próprio uma expressão constante. (Isso é possível quando o endereço é gerado aplicando o operador address a uma expressão constante estática / global.) Aqui, ambos constexpre constsão necessários: constexprsempre se refere à expressão que está sendo declarada (aqui NP), enquanto constse refere a int(ela declara um ponteiro- para const). A remoção de consttornaria a expressão ilegal (porque (a) um ponteiro para um objeto não const não pode ser uma expressão constante e (b) &Né de fato um ponteiro para constante).

B. Nas declarações de função membro. Em C ++ 11, constexprimplica const, enquanto em C ++ 14 e C ++ 17 esse não é o caso. Uma função de membro declarada em C ++ 11 como

constexpr void f();

precisa ser declarado como

constexpr void f() const;

no C ++ 14, a fim de ainda ser utilizável como uma constfunção.

jogojapan
fonte
3
A IMO "não necessariamente avaliada em tempo de compilação" é menos útil do que pensar nelas como "avaliada em tempo de compilação". As restrições em uma expressão constante significam que seria relativamente fácil para um compilador avaliá-lo. Um compilador deve reclamar se essas restrições não forem atendidas. Como não há efeitos colaterais, você nunca pode dizer a diferença se um compilador a "avaliou" ou não.
aschepler
10
@aschepler Claro. Meu ponto principal é que, se você chamar uma constexprfunção em uma expressão não constante, por exemplo, uma variável comum, isso é perfeitamente legal e a função será usada como qualquer outra função. Não será avaliado em tempo de compilação (porque não pode). Talvez você ache isso óbvio - mas se eu declarasse que uma função declarada como constexprsempre será avaliada em tempo de compilação, ela poderá ser interpretada da maneira errada.
precisa saber é
5
Sim, eu estava falando sobre constexprobjetos, não funções. Eu gosto de pensar constexprnos objetos como forçando a avaliação de valores no tempo de compilação e constexprnas funções como permitindo que a função seja avaliada no tempo de compilação ou no tempo de execução, conforme apropriado.
aschepler
2
Uma correção: 'const' é apenas uma restrição que você NÃO pode alterar o valor de uma variável; não promete que o valor não mude (ou seja, por outra pessoa). É uma propriedade de gravação, não uma propriedade de leitura.
precisa saber é o seguinte
3
Esta frase: garante que a função de membro não modifique nenhum dos membros de dados não estáticos. perde um detalhe importante. Membros marcados como mutabletambém podem ser modificados por constfunções de membro.
Onipotente 12/0318
119

constaplica-se a variáveis e impede que elas sejam modificadas no seu código.

constexprinforma ao compilador que essa expressão resulta em um valor constante de tempo de compilação , para que possa ser usada em locais como comprimentos de matriz, atribuindo a constvariáveis ​​etc. O link fornecido por Oli possui muitos exemplos excelentes.

Basicamente, eles são dois conceitos diferentes e podem (e devem) ser usados ​​juntos.

Karthik T
fonte
2
const & constexpr uso, ex: en.cppreference.com/w/cpp/container/array/get
Manohar Reddy Poreddy
64

Visão geral

  • constgarante que um programa não altere o valor de um objeto . No entanto, constnão garante que tipo de inicialização o objeto passa.

    Considerar:

    const int mx = numeric_limits<int>::max();  // OK: runtime initialization

    A função max()simplesmente retorna um valor literal. No entanto, como o inicializador é uma chamada de função, mxpassa pela inicialização do tempo de execução. Portanto, você não pode usá-lo como uma expressão constante :

    int arr[mx];  // error: “constant expression required”
  • constexpré uma nova palavra-chave do C ++ 11 que libera a necessidade de criar macros e literais codificados. Também garante, sob certas condições, que os objetos passem por inicialização estática . Controla o tempo de avaliação de uma expressão. Ao impor a avaliação em tempo de compilação de sua expressão , constexprpermite definir expressões constantes verdadeiras que são cruciais para aplicativos de tempo crítico, programação do sistema, modelos e, em geral, em qualquer código que dependa de constantes em tempo de compilação.

Funções de expressão constante

Uma função de expressão constante é uma função declarada constexpr. Seu corpo deve ser não virtual e consistir em apenas uma única declaração de retorno, além de typedefs e afirmações estáticas. Seus argumentos e valor de retorno devem ter tipos literais. Pode ser usado com argumentos de expressão não constante, mas quando isso é feito, o resultado não é uma expressão constante.

Uma função de expressão constante visa substituir macros e literais codificados sem sacrificar o desempenho ou a segurança do tipo.

constexpr int max() { return INT_MAX; }           // OK
constexpr long long_max() { return 2147483647; }  // OK
constexpr bool get_val()
{
    bool res = false;
    return res;
}  // error: body is not just a return statement

constexpr int square(int x)
{ return x * x; }  // OK: compile-time evaluation only if x is a constant expression
const int res = square(5);  // OK: compile-time evaluation of square(5)
int y = getval();
int n = square(y);          // OK: runtime evaluation of square(y)

Objetos de expressão constante

Um objeto de expressão constante é um objeto declarado constexpr. Ele deve ser inicializado com uma expressão constante ou um rvalue construído por um construtor de expressão constante com argumentos de expressão constante.

Um objeto de expressão constante se comporta como se tivesse sido declarado const, exceto que requer inicialização antes do uso e seu inicializador deve ser uma expressão constante. Consequentemente, um objeto de expressão constante sempre pode ser usado como parte de outra expressão constante.

struct S
{
    constexpr int two();      // constant-expression function
private:
    static constexpr int sz;  // constant-expression object
};
constexpr int S::sz = 256;
enum DataPacket
{
    Small = S::two(),  // error: S::two() called before it was defined
    Big = 1024
};
constexpr int S::two() { return sz*2; }
constexpr S s;
int arr[s.two()];  // OK: s.two() called after its definition

Construtores de expressão constante

Um construtor de expressão constante é um construtor declarado constexpr. Pode ter uma lista de inicialização de membros, mas seu corpo deve estar vazio, além de typedefs e afirmações estáticas. Seus argumentos devem ter tipos literais.

Um construtor de expressão constante permite que o compilador inicialize o objeto em tempo de compilação, desde que os argumentos do construtor sejam expressões constantes.

struct complex
{
    // constant-expression constructor
    constexpr complex(double r, double i) : re(r), im(i) { }  // OK: empty body
    // constant-expression functions
    constexpr double real() { return re; }
    constexpr double imag() { return im; }
private:
    double re;
    double im;
};
constexpr complex COMP(0.0, 1.0);         // creates a literal complex
double x = 1.0;
constexpr complex cx1(x, 0);              // error: x is not a constant expression
const complex cx2(x, 1);                  // OK: runtime initialization
constexpr double xx = COMP.real();        // OK: compile-time initialization
constexpr double imaglval = COMP.imag();  // OK: compile-time initialization
complex cx3(2, 4.6);                      // OK: runtime initialization

Dicas do livro Effective Modern C ++, de Scott Meyers, sobre constexpr:

  • constexpr objetos são const e são inicializados com valores conhecidos durante a compilação;
  • constexpr funções produzem resultados em tempo de compilação quando chamados com argumentos cujos valores são conhecidos durante a compilação;
  • constexprobjetos e funções podem ser usados ​​em uma ampla gama de contextos que não constexprobjetos e funções;
  • constexpr faz parte da interface de um objeto ou função.

Fonte: Usando constexpr para melhorar a segurança, desempenho e encapsulamento em C ++ .

zangw
fonte
Obrigado pelo ótimo exemplo de código que mostra as diferentes situações. Por mais excelentes que sejam algumas das outras explicações, achei o código em ação muito mais útil e compreensível. Isso realmente ajudou a solidificar minha compreensão do que está acontecendo.
precisa saber é o seguinte
35

De acordo com o livro de "The C ++ Programming Language 4th Editon", de Bjarne Stroustrup
const : significando aproximadamente '' Eu prometo não mudar esse valor '' (§7.5). Isso é usado principalmente para especificar interfaces, para que os dados possam ser transmitidos para funções sem medo de serem modificados.
O compilador reforça a promessa feita pela const.
constexpr : significando aproximadamente '' ser avaliado em tempo de compilação '' (§10.4). Isso é usado principalmente para especificar constantes, para permitir
Por exemplo:

const int dmv = 17; // dmv is a named constant
int var = 17; // var is not a constant
constexpr double max1 = 1.4*square(dmv); // OK if square(17) is a constant expression
constexpr double max2 = 1.4square(var); // error : var is not a constant expression
const double max3 = 1.4square(var); //OK, may be evaluated at run time
double sum(const vector<double>&); // sum will not modify its argument (§2.2.5)
vector<double> v {1.2, 3.4, 4.5}; // v is not a constant
const double s1 = sum(v); // OK: evaluated at run time
constexpr double s2 = sum(v); // error : sum(v) not constant expression

Para que uma função seja utilizável em uma expressão constante, ou seja, em uma expressão que será avaliada pelo compilador, ela deve ser definida como constexpr .
Por exemplo:

constexpr double square(double x) { return xx; }


Para ser constexpr, uma função deve ser bastante simples: apenas uma declaração de retorno computando um valor. Uma função constexpr pode ser usada para argumentos não constantes, mas quando isso é feito, o resultado não é uma expressão constante. Permitimos que uma função constexpr seja chamada com argumentos de expressão não constante em contextos que não exigem expressões constantes, para que não tenhamos de definir essencialmente a mesma função duas vezes: uma para expressões constantes e outra para variáveis.
Em alguns lugares, expressões constantes são exigidas pelas regras de linguagem (por exemplo, limites de array (§2.2.5, §7.3), rótulos de caso (§2.2.4, §9.4.2), alguns argumentos de modelo (§25.2) e constantes declaradas usando constexpr). Em outros casos, a avaliação em tempo de compilação é importante para o desempenho. Independentemente das questões de desempenho, a noção de imutabilidade (de um objeto com um estado imutável) é uma importante preocupação de design (§10.4).

Mustafa Ekici
fonte
ainda existem problemas de desempenho. Parece que a função constexpr, se avaliada em tempo de execução, pode ser mais lenta que a versão da função não constexpr. Além disso, se tivermos um valor constante, devemos preferir "const" ou "constexpr"? (mais uma questão de estilo gerado montagem parece o mesmo)
CoffeDeveloper
31

Ambos conste constexprpodem ser aplicados a variáveis ​​e funções. Mesmo sendo semelhantes, na verdade são conceitos muito diferentes.

Ambos conste constexprsignificam que seus valores não podem ser alterados após a inicialização. Então, por exemplo:

const int x1=10;
constexpr int x2=10;

x1=20; // ERROR. Variable 'x1' can't be changed.
x2=20; // ERROR. Variable 'x2' can't be changed.

A principal diferença entre conste constexpré o momento em que seus valores de inicialização são conhecidos (avaliados). Embora os valores das constvariáveis ​​possam ser avaliados no tempo de compilação e no tempo de execução, constexpreles sempre são avaliados no tempo de compilação. Por exemplo:

int temp=rand(); // temp is generated by the the random generator at runtime.

const int x1=10; // OK - known at compile time.
const int x2=temp; // OK - known only at runtime.
constexpr int x3=10; // OK - known at compile time.
constexpr int x4=temp; // ERROR. Compiler can't figure out the value of 'temp' variable at compile time so `constexpr` can't be applied here.

A principal vantagem de saber se o valor é conhecido em tempo de compilação ou tempo de execução é o fato de que constantes de tempo de compilação podem ser usadas sempre que são necessárias constantes de tempo de compilação. Por exemplo, o C ++ não permite especificar matrizes C com os comprimentos variáveis.

int temp=rand(); // temp is generated by the the random generator at runtime.

int array1[10]; // OK.
int array2[temp]; // ERROR.

Então isso significa que:

const int size1=10; // OK - value known at compile time.
const int size2=temp; // OK - value known only at runtime.
constexpr int size3=10; // OK - value known at compile time.


int array3[size1]; // OK - size is known at compile time.
int array4[size2]; // ERROR - size is known only at runtime time.
int array5[size3]; // OK - size is known at compile time.

Portanto, as constvariáveis ​​podem definir constantes de tempo de compilação como size1essas que podem ser usadas para especificar tamanhos de matriz e constantes de tempo de execução como size2essas conhecidas apenas em tempo de execução e não podem ser usadas para definir tamanhos de matriz. Por outro ladoconstexpr defina sempre constantes de tempo de compilação que podem especificar tamanhos de matriz.

Ambos conste também constexprpodem ser aplicados a funções. Uma constfunção deve ser uma função membro (método, operador) em que a aplicação da constpalavra-chave significa que o método não pode alterar os valores de seus campos membros (não estáticos). Por exemplo.

class test
{
   int x;

   void function1()
   {
      x=100; // OK.
   }

   void function2() const
   {
      x=100; // ERROR. The const methods can't change the values of object fields.
   }
};

A constexpré um conceito diferente. Ele marca uma função (membro ou não membro) como a função que pode ser avaliada em tempo de compilação se constantes de tempo de compilação forem passadas como argumentos . Por exemplo, você pode escrever isso.

constexpr int func_constexpr(int X, int Y)
{
    return(X*Y);
}

int func(int X, int Y)
{
    return(X*Y);
}

int array1[func_constexpr(10,20)]; // OK - func_constexpr() can be evaluated at compile time.
int array2[func(10,20)]; // ERROR - func() is not a constexpr function.

int array3[func_constexpr(10,rand())]; // ERROR - even though func_constexpr() is the 'constexpr' function, the expression 'constexpr(10,rand())' can't be evaluated at compile time.

A propósito, as constexprfunções são as funções regulares do C ++ que podem ser chamadas mesmo que argumentos não constantes sejam passados. Mas, nesse caso, você está obtendo os valores não constexpr.

int value1=func_constexpr(10,rand()); // OK. value1 is non-constexpr value that is evaluated in runtime.
constexpr int value2=func_constexpr(10,rand()); // ERROR. value2 is constexpr and the expression func_constexpr(10,rand()) can't be evaluated at compile time.

o constexpr pode ser aplicado às funções membro (métodos), operadores e até construtores. Por exemplo.

class test2
{
    static constexpr int function(int value)
    {
        return(value+1);
    }

    void f()
    {
        int x[function(10)];


    }
};

Uma amostra mais 'louca'.

class test3
{
    public:

    int value;

    // constexpr const method - can't chanage the values of object fields and can be evaluated at compile time.
    constexpr int getvalue() const
    {
        return(value);
    }

    constexpr test3(int Value)
        : value(Value)
    {
    }
};


constexpr test3 x(100); // OK. Constructor is constexpr.

int array[x.getvalue()]; // OK. x.getvalue() is constexpr and can be evaluated at compile time.
Timmy_A
fonte
Além disso, em C, constexpr intexiste , mas está escritoconst int
curiousguy
8

Como @ 0x499602d2 já apontou, constapenas garante que um valor não possa ser alterado após a inicialização, onde como constexpr(introduzido no C ++ 11) garante que a variável é uma constante de tempo de compilação.
Considere o seguinte exemplo (do LearnCpp.com):

cout << "Enter your age: ";
int age;
cin >> age;

const int myAge{age};        // works
constexpr int someAge{age};  // error: age can only be resolved at runtime
Lokesh Meher
fonte
5

A const int varpode ser configurado dinamicamente para um valor em tempo de execução e, uma vez definido para esse valor, não pode mais ser alterado.

UMA constexpr int var não pode ser definido dinamicamente em tempo de execução, mas em tempo de compilação. E uma vez definido para esse valor, ele não poderá mais ser alterado.

Aqui está um exemplo sólido:

int main(int argc, char*argv[]) {
    const int p = argc; 
    // p = 69; // cannot change p because it is a const
    // constexpr int q = argc; // cannot be, bcoz argc cannot be computed at compile time 
    constexpr int r = 2^3; // this works!
    // r = 42; // same as const too, it cannot be changed
}

O snippet acima compila bem e eu comentei aqueles que causam erros.

As noções-chave aqui a serem observadas são as noções de compile timee run time. Novas inovações foram introduzidas no C ++ destinadas a, tanto quanto possível, ** know **certas coisas no momento da compilação para melhorar o desempenho no tempo de execução.

truthadjustr
fonte
3

Eu não acho que nenhuma das respostas realmente deixe claro exatamente quais são os efeitos colaterais, ou mesmo quais são.

constexpre constno espaço de nome / escopo do arquivo são idênticos quando inicializados com um literal ou expressão; mas com uma função, constpode ser inicializado por qualquer função, mas constexprinicializado por um não constexpr (uma função que não está marcada com constexpr ou uma expressão não constexpr) gerará um erro do compilador. Ambos constexpre constsão ligação implicitamente interna para variáveis (bem, na verdade, eles não sobrevivem para chegar ao estágio que liga se compilar -O1 e mais forte, e staticnão forçar o compilador para emitir um local) símbolo interna (vinculador para constou constexprquando pelo -O1 ou mais forte; o único momento em que faz isso é se você pegar o endereço da variável conste constexprserá um símbolo interno, a menos que seja expresso com externieextern constexpr/const int i = 3;precisa ser usado). Em uma função,constexprfaz com que a função permanentemente nunca atinja o estágio de vinculação (independentemente externou inlinena definição ou -O0 ou -Ofast), ao passo que constnunca atinge statice inlineapenas tem esse efeito em -O1 e acima. Quando uma variável const/ constexpré inicializada por uma constexprfunção, a carga é sempre otimizada com qualquer sinalizador de otimização, mas nunca é otimizada se a função for apenas staticou inline, ou se a variável não for const/ constexpr.

Compilação padrão (-O0)

#include<iostream>
constexpr int multiply (int x, int y)
{

  return x * y;
}

extern const int val = multiply(10,10);
int main () {
  std::cout << val;
} 

compila para

val:
        .long   100  //extra external definition supplied due to extern

main:
        push    rbp
        mov     rbp, rsp
        mov     esi, 100 //substituted in as an immediate
        mov     edi, OFFSET FLAT:_ZSt4cout
        call    std::basic_ostream<char, std::char_traits<char> >::operator<<(int)
        mov     eax, 0
        pop     rbp
        ret

__static_initialization_and_destruction_0(int, int):
        . 
        . 
        . 

Contudo

#include<iostream>
const int multiply (int x, int y)
{

  return x * y;
}

const int val = multiply(10,10); //constexpr is an error
int main () {
  std::cout << val;
}

Compila para

multiply(int, int):
        push    rbp
        mov     rbp, rsp
        mov     DWORD PTR [rbp-4], edi
        mov     DWORD PTR [rbp-8], esi
        mov     eax, DWORD PTR [rbp-4]
        imul    eax, DWORD PTR [rbp-8]
        pop     rbp
        ret

main:
        push    rbp
        mov     rbp, rsp
        mov     eax, DWORD PTR val[rip]
        mov     esi, eax
        mov     edi, OFFSET FLAT:_ZSt4cout
        call    std::basic_ostream<char, std::char_traits<char> >::operator<<(int)
        mov     eax, 0
        pop     rbp
        ret

__static_initialization_and_destruction_0(int, int):
        . 
        . 
        . 
        mov     esi, 10
        mov     edi, 10
        call    multiply(int, int)
        mov     DWORD PTR val[rip], eax

Isso mostra claramente que constexprfaz com que a inicialização da const/constexprvariável de escopo do arquivo ocorra no momento da compilação e não produza nenhum símbolo global, enquanto não usá-la faz com que a inicialização ocorra antes mainno tempo de execução.

Compilando usando -Ofast

Mesmo -Ofast não otimiza a carga! https://godbolt.org/z/r-mhif , então você precisa constexpr


constexprfunções também podem ser chamadas de dentro de outras constexprfunções para o mesmo resultado. constexprem uma função também impede o uso de qualquer coisa que não possa ser feita em tempo de compilação na função; por exemplo, uma chamada para o <<operador std::cout.

constexprno escopo do bloco se comporta da mesma maneira em que produz um erro se inicializado por uma função não constexpr; o valor também é substituído imediatamente.

No final, seu principal objetivo é como a função embutida de C, mas só é eficaz quando a função é usada para inicializar variáveis ​​de escopo de arquivo (que funções não podem ser executadas em C, mas sim em C ++ porque permitem a inicialização dinâmica de arquivos) variáveis ​​de escopo), exceto que a função também não pode exportar um símbolo global / local para o vinculador, mesmo usando extern/static, o que você poderia fazer inlineem C; as funções de atribuição de variáveis ​​de escopo de bloco podem ser incorporadas simplesmente usando uma otimização -O1 sem constexprem C e C ++.

Lewis Kelsey
fonte
Bom ponto no vinculador. Em geral, seria mais seguro usar o constexpr, pois resulta em menos vazamentos de símbolos?
Neil McGill
1
@NeilMcGill, na verdade, porque inline e estático fará com que o compilador não emita um símbolo local para multiplicar se estiver compilando usando -O1 ou mais forte. O Constexpr é o único que otimiza a carga para val, mas, além disso, é idêntico a colocar estática ou inline antes da função. Também esqueci outra coisa. Constexpr é a única palavra-chave que não emite um símbolo para a função em -O0, estático e em linha
Lewis Kelsey
1

Primeiro de tudo, ambos são qualificadores em c ++. Uma variável const declarada deve ser inicializada e não pode ser alterada no futuro. Portanto, geralmente uma variável declarada como const terá um valor antes mesmo da compilação.

Mas, para o constexpr, é um pouco diferente.

Para constexpr, você pode fornecer uma expressão que possa ser avaliada durante a compilação do programa.

Obviamente, a variável declarada como constexper não pode ser alterada no futuro, assim como const.

Subhash Malireddy
fonte