@ 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 expressionint 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(){constint 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>classlist{};constexprint sqr1(int arg){return arg * arg;}int sqr2(int arg){return arg * arg;}int main(){constint X =2;list<sqr1(X)> mylist1;// OK: sqr1 is constexprlist<sqr2(X)> mylist2;// wrong: sqr2 is not constexpr}
Quando posso / devo usar os dois conste constexprjuntos?
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.
constexprconstint N =5;
é o mesmo que
constexprint N =5;
No entanto, observe que pode haver situações em que as palavras-chave se referem a diferentes partes da declaração:
staticconstexprint N =3;int main(){constexprconstint*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
constexprvoid f();
precisa ser declarado como
constexprvoid f()const;
no C ++ 14, a fim de ainda ser utilizável como uma constfunção.
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.
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.
constexprint max(){return INT_MAX;}// OKconstexprlong long_max(){return2147483647;}// OKconstexprbool get_val(){bool res =false;return res;}// error: body is not just a return statementconstexprint square(int x){return x * x;}// OK: compile-time evaluation only if x is a constant expressionconstint 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
{constexprint two();// constant-expression functionprivate:staticconstexprint sz;// constant-expression object};constexprint S::sz =256;enumDataPacket{Small= S::two(),// error: S::two() called before it was definedBig=1024};constexprint 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 constructorconstexpr complex(double r,double i): re(r), im(i){}// OK: empty body// constant-expression functionsconstexprdouble real(){return re;}constexprdouble imag(){return im;}private:double re;double im;};constexpr complex COMP(0.0,1.0);// creates a literal complexdouble x =1.0;constexpr complex cx1(x,0);// error: x is not a constant expressionconst complex cx2(x,1);// OK: runtime initializationconstexprdouble xx = COMP.real();// OK: compile-time initializationconstexprdouble 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.
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:
constint dmv =17;// dmv is a named constantint var =17;// var is not a constantconstexprdouble max1 =1.4*square(dmv);// OK if square(17) is a constant expressionconstexprdouble max2 =1.4∗square(var);// error : var is not a constant expressionconstdouble max3 =1.4∗square(var);//OK, may be evaluated at run timedouble sum(constvector<double>&);// sum will not modify its argument (§2.2.5)vector<double> v {1.2,3.4,4.5};// v is not a constantconstdouble s1 = sum(v);// OK: evaluated at run timeconstexprdouble 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:
constexprdouble square(double x){return x∗x;}
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).
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:
constint x1=10;constexprint 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.constint x1=10;// OK - known at compile time.constint x2=temp;// OK - known only at runtime.constexprint x3=10;// OK - known at compile time.constexprint 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:
constint size1=10;// OK - value known at compile time.constint size2=temp;// OK - value known only at runtime.constexprint 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.
constexprint 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.constexprint 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
{staticconstexprint 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.constexprint getvalue()const{return(value);}constexpr test3(intValue): value(Value){}};constexpr test3 x(100);// OK. Constructor is constexpr.intarray[x.getvalue()];// OK. x.getvalue() is constexpr and can be evaluated at compile time.
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;constint myAge{age};// worksconstexprint someAge{age};// error: age can only be resolved at runtime
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[]){constint 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 constexprint 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.
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>constexprint multiply (int x,int y){return x * y;}externconstint val = multiply(10,10);int main (){
std::cout << val;}
compila para
val:.long100//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>constint multiply (int x,int y){return x * y;}constint val = multiply(10,10);//constexpr is an errorint 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.
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 ++.
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.
constexpr
cria uma constante em tempo de compilação;const
significa simplesmente que o valor não pode ser alterado.boost/hana
biblioteca possa esclarecer algunsconstexpr
problemas nos quais você pode usarconstexpr
e onde não pode: boost.org/doc/libs/1_69_0/libs/hana/doc/html/…constexpr
época também :)Respostas:
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:
const
declara 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.constexpr
declara um objeto como apto para uso no que o Padrão chama de expressões constantes . Mas observe queconstexpr
não é a única maneira de fazer isso.Quando aplicada a funções, a diferença básica é esta:
const
só 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.constexpr
pode 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 (†) :return
instrução é permitida. No caso de um construtor, apenas uma lista de inicialização, typedefs e declaração estática são permitidos. (= default
e também= delete
são permitidos.)asm
declaração, umagoto
instrução, uma instrução com um rótulo diferente decase
edefault
, 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.Expressões constantes
Como dito acima,
constexpr
declara 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:
Mas observe:
Declarar algo como
constexpr
nã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: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 declaradaconstexpr
.Então, quando eu realmente tenho que usar
constexpr
?Um objeto como o
N
acima pode ser usado como expressão constante sem ser declaradoconstexpr
. Isso vale para todos os objetos que são:const
[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:Quando posso / devo usar os dois
const
econstexpr
juntos?A. Nas declarações de objetos. Isso nunca é necessário quando as duas palavras-chave se referem ao mesmo objeto a ser declarado.
constexpr
implicaconst
.é o mesmo que
No entanto, observe que pode haver situações em que as palavras-chave se referem a diferentes partes da declaração:
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, ambosconstexpr
econst
são necessários:constexpr
sempre se refere à expressão que está sendo declarada (aquiNP
), enquantoconst
se refere aint
(ela declara um ponteiro- para const). A remoção deconst
tornaria 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,
constexpr
implicaconst
, enquanto em C ++ 14 e C ++ 17 esse não é o caso. Uma função de membro declarada em C ++ 11 comoprecisa ser declarado como
no C ++ 14, a fim de ainda ser utilizável como uma
const
função.fonte
constexpr
funçã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 comoconstexpr
sempre será avaliada em tempo de compilação, ela poderá ser interpretada da maneira errada.constexpr
objetos, não funções. Eu gosto de pensarconstexpr
nos objetos como forçando a avaliação de valores no tempo de compilação econstexpr
nas funções como permitindo que a função seja avaliada no tempo de compilação ou no tempo de execução, conforme apropriado.mutable
também podem ser modificados porconst
funções de membro.const
aplica-se a variáveis e impede que elas sejam modificadas no seu código.constexpr
informa 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 aconst
variá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.
fonte
Visão geral
const
garante que um programa não altere o valor de um objeto . No entanto,const
não garante que tipo de inicialização o objeto passa.Considerar:
A função
max()
simplesmente retorna um valor literal. No entanto, como o inicializador é uma chamada de função,mx
passa pela inicialização do tempo de execução. Portanto, você não pode usá-lo como uma expressão constante :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 ,constexpr
permite 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.
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.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.
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;constexpr
objetos e funções podem ser usados em uma ampla gama de contextos que nãoconstexpr
objetos 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 ++ .
fonte
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:
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:
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).
fonte
Ambos
const
econstexpr
podem ser aplicados a variáveis e funções. Mesmo sendo semelhantes, na verdade são conceitos muito diferentes.Ambos
const
econstexpr
significam que seus valores não podem ser alterados após a inicialização. Então, por exemplo:A principal diferença entre
const
econstexpr
é o momento em que seus valores de inicialização são conhecidos (avaliados). Embora os valores dasconst
variáveis possam ser avaliados no tempo de compilação e no tempo de execução,constexpr
eles sempre são avaliados no tempo de compilação. Por exemplo: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.
Então isso significa que:
Portanto, as
const
variáveis podem definir constantes de tempo de compilação comosize1
essas que podem ser usadas para especificar tamanhos de matriz e constantes de tempo de execução comosize2
essas 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
const
e tambémconstexpr
podem ser aplicados a funções. Umaconst
função deve ser uma função membro (método, operador) em que a aplicação daconst
palavra-chave significa que o método não pode alterar os valores de seus campos membros (não estáticos). Por exemplo.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.A propósito, as
constexpr
funçõ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.o
constexpr
pode ser aplicado às funções membro (métodos), operadores e até construtores. Por exemplo.Uma amostra mais 'louca'.
fonte
constexpr int
existe , mas está escritoconst int
Como @ 0x499602d2 já apontou,
const
apenas garante que um valor não possa ser alterado após a inicialização, onde comoconstexpr
(introduzido no C ++ 11) garante que a variável é uma constante de tempo de compilação.Considere o seguinte exemplo (do LearnCpp.com):
fonte
A
const int var
pode 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:
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 time
erun 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.fonte
Eu não acho que nenhuma das respostas realmente deixe claro exatamente quais são os efeitos colaterais, ou mesmo quais são.
constexpr
econst
no espaço de nome / escopo do arquivo são idênticos quando inicializados com um literal ou expressão; mas com uma função,const
pode ser inicializado por qualquer função, masconstexpr
inicializado 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. Ambosconstexpr
econst
sã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, estatic
não forçar o compilador para emitir um local) símbolo interna (vinculador paraconst
ouconstexpr
quando pelo -O1 ou mais forte; o único momento em que faz isso é se você pegar o endereço da variávelconst
econstexpr
será um símbolo interno, a menos que seja expresso comextern
ieextern constexpr/const int i = 3;
precisa ser usado). Em uma função,constexpr
faz com que a função permanentemente nunca atinja o estágio de vinculação (independentementeextern
ouinline
na definição ou -O0 ou -Ofast), ao passo queconst
nunca atingestatic
einline
apenas tem esse efeito em -O1 e acima. Quando uma variávelconst
/constexpr
é inicializada por umaconstexpr
função, a carga é sempre otimizada com qualquer sinalizador de otimização, mas nunca é otimizada se a função for apenasstatic
ouinline
, ou se a variável não forconst
/constexpr
.Compilação padrão (-O0)
compila para
Contudo
Compila para
Isso mostra claramente que
constexpr
faz com que a inicialização daconst/constexpr
variá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 antesmain
no 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
constexpr
funções também podem ser chamadas de dentro de outrasconstexpr
funções para o mesmo resultado.constexpr
em 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<<
operadorstd::cout
.constexpr
no 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 fazerinline
em C; as funções de atribuição de variáveis de escopo de bloco podem ser incorporadas simplesmente usando uma otimização -O1 semconstexpr
em C e C ++.fonte
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.
fonte