Puede ser que este artículo de la boost/hanabiblioteca pueda aclarar algunos constexprproblemas donde puede usar constexpry donde no puede: boost.org/doc/libs/1_69_0/libs/hana/doc/html/…
Andry
@ 0x499602D2 " simplemente significa que el valor no se puede cambiar " Para un escalar inicializado con un literal, un valor que no se puede cambiar también es una constante de tiempo de compilación.
curioso
@curiousguy Sí, mi comentario fue muy simplificado. Es cierto que también era nuevo en constexpraquel entonces :)
0x499602D2
Respuestas:
587
Significado básico y sintaxis
Ambas palabras clave se pueden utilizar en la declaración de objetos y funciones. La diferencia básica cuando se aplica a los objetos es esta:
constdeclara un objeto como constante . Esto implica una garantía de que una vez inicializado, el valor de ese objeto no cambiará, y el compilador puede hacer uso de este hecho para optimizaciones. También ayuda a evitar que el programador escriba código que modifique objetos que no debían modificarse después de la inicialización.
constexprdeclara un objeto apto para su uso en lo que el estándar llama expresiones constantes . Pero tenga en cuenta que constexprno es la única forma de hacer esto.
Cuando se aplica a las funciones, la diferencia básica es esta:
constsolo se puede usar para funciones miembro no estáticas, no funciones en general. Da una garantía de que la función miembro no modifica ninguno de los miembros de datos no estáticos.
constexprse puede usar con funciones miembro y no miembro, así como con constructores. Declara la función apta para su uso en expresiones constantes . El compilador solo lo aceptará si la función cumple con ciertos criterios (7.1.5 / 3,4), lo más importante (†) :
El cuerpo de la función debe ser no virtual y extremadamente simple: además de los typedefs y las returnafirmaciones estáticas, solo se permite una sola declaración. En el caso de un constructor, solo se permiten una lista de inicialización, typedefs y aserción estática. (= default y= delete están permitidos).
A partir de C ++ 14, las reglas son más relajadas, lo que está permitido desde entonces dentro de una función constexpr: asmdeclaración, una gotodeclaración, una declaración con una etiqueta que no sea caseydefault , try-block, la definición de una variable no literal tipo, definición de una variable de duración de almacenamiento estático o de subprocesos, la definición de una variable para la que no se realiza ninguna inicialización.
Los argumentos y el tipo de retorno deben ser tipos literales (es decir, en términos generales, tipos muy simples, típicamente escalares o agregados)
Expresiones constantes
Como se dijo anteriormente, constexprdeclara que tanto los objetos como las funciones son aptos para su uso en expresiones constantes. Una expresión constante es más que simplemente constante:
Se puede usar en lugares que requieren evaluación en tiempo de compilación, por ejemplo, parámetros de plantilla y especificadores de tamaño 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
Pero nota:
Declarar algo como constexprno necesariamente garantiza que se evaluará en tiempo de compilación. Se puede usar para eso, pero también se puede usar en otros lugares que se evalúan en tiempo de ejecución.
Un objeto puede ser apto para su uso en expresiones constantes sin ser declarado constexpr. Ejemplo:
int main(){constint N =3;int numbers[N]={1,2,3};// N is constant expression}
Esto es posible porque N, al ser constante e inicializado en el momento de la declaración con un literal, satisface los criterios para una expresión constante, incluso si no se declara constexpr.
Entonces, ¿cuándo debo usarlo constexpr?
Un objeto como el Nanterior se puede usar como expresión constante sin ser declarado constexpr. Esto es cierto para todos los objetos que son:
const
de tipo integral o de enumeración y
inicializado en el momento de la declaración con una expresión que es en sí misma una expresión constante
[Esto se debe a §5.19 / 2: una expresión constante no debe incluir una subexpresión que implique "una modificación de valor a valor a menos que [...] un valor de tipo integral o enumeración [...]" Gracias a Richard Smith por corregir mi afirmación anterior de que esto era cierto para todos los tipos literales.]
Para que una función sea apta para su uso en expresiones constantes, debe declararse explícitamente constexpr; no es suficiente simplemente para satisfacer los criterios para las funciones de expresión constante. Ejemplo:
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}
A. En declaraciones de objeto. Esto nunca es necesario cuando ambas palabras clave se refieren al mismo objeto a declarar. constexprimplica const.
constexprconstint N =5;
es lo mismo que
constexprint N =5;
Sin embargo, tenga en cuenta que puede haber situaciones en las que las palabras clave se refieren a diferentes partes de la declaración:
staticconstexprint N =3;int main(){constexprconstint*NP =&N;}
Aquí, NPse declara como una dirección de expresión constante, es decir, un puntero que es en sí mismo una expresión constante. (Esto es posible cuando la dirección se genera aplicando el operador de dirección a una expresión constante estática / global). Aquí, ambos constexpry constson obligatorios: constexprsiempre se refiere a la expresión que se declara (aquí NP), mientras que se constrefiere a int(declara un puntero- a-const). La eliminación de constharía que la expresión sea ilegal (porque (a) un puntero a un objeto no constante no puede ser una expresión constante, y (b) &Nes, de hecho, un puntero a constante).
B. En declaraciones de funciones miembro. En C ++ 11, constexprimplica const, mientras que en C ++ 14 y C ++ 17 ese no es el caso. Una función miembro declarada bajo C ++ 11 como
constexprvoid f();
necesita ser declarado como
constexprvoid f()const;
bajo C ++ 14 para poder seguir siendo utilizable como una constfunción.
En mi opinión, "no necesariamente evaluado en tiempo de compilación" es menos útil que pensar en ellos como "evaluado en tiempo de compilación". Las restricciones en una expresión constante significan que sería relativamente fácil para un compilador evaluarla. Un compilador debe quejarse si esas restricciones no se cumplen. Como no hay efectos secundarios, nunca se puede saber si un compilador lo "evaluó" o no.
Aschepler
10
@aschepler Claro. Mi punto principal es que si llama a una constexprfunción en una expresión no constante, por ejemplo, una variable ordinaria, esto es perfectamente legal y la función se utilizará como cualquier otra función. No se evaluará en tiempo de compilación (porque no puede). Tal vez piense que es obvio, pero si afirmo que una función declarada como constexprsiempre se evaluará en tiempo de compilación, podría interpretarse de manera incorrecta.
jogojapan
55
Sí, estaba hablando de constexprobjetos, no de funciones. Me gusta pensar que los constexprobjetos fuerzan la evaluación de valores en tiempo de compilación, y que las constexprfunciones permiten evaluar la función en tiempo de compilación o en tiempo de ejecución según corresponda.
Aschepler
2
Una corrección: 'const' es solo una restricción de que USTED no puede cambiar el valor de una variable; no promete que el valor no cambiará (es decir, por otra persona). Es una propiedad de escritura, no una propiedad de lectura.
Jared Grubb
3
Esta oración: garantiza que la función miembro no modifica ninguno de los miembros de datos no estáticos. echa de menos un detalle importante. Los miembros marcados como mutabletambién pueden ser modificados por las constfunciones del miembro.
Omnifarious
119
constaplica para las variables y evita que se modifiquen en su código.
constexprle dice al compilador que esta expresión da como resultado un valor constante de tiempo de compilación , por lo que puede usarse en lugares como longitudes de matriz, asignación a constvariables, etc. El enlace proporcionado por Oli tiene muchos ejemplos excelentes.
Básicamente son 2 conceptos diferentes en total, y pueden (y deberían) usarse juntos.
La función max()simplemente devuelve un valor literal. Sin embargo, debido a que el inicializador es una llamada de función, se mxsomete a una inicialización de tiempo de ejecución. Por lo tanto, no puede usarlo como una expresión constante :
int arr[mx];// error: “constant expression required”
constexpres una nueva palabra clave de C ++ 11 que elimina la necesidad de crear macros y literales codificados. También garantiza, bajo ciertas condiciones, que los objetos se someten a una inicialización estática . Controla el tiempo de evaluación de una expresión. Al aplicar la evaluación de su expresión en tiempo de compilación , le constexprpermite definir expresiones constantes verdaderas que son cruciales para aplicaciones críticas de tiempo, programación del sistema, plantillas y, en general, en cualquier código que se base en constantes de tiempo de compilación.
Funciones de expresión constante
Una función de expresión constante es una función declarada constexpr. Su cuerpo debe ser no virtual y consistir en una sola declaración de retorno, además de typedefs y aserciones estáticas. Sus argumentos y valor de retorno deben tener tipos literales. Se puede usar con argumentos de expresión no constante, pero cuando eso se hace, el resultado no es una expresión constante.
Una función de expresión constante está destinada a reemplazar macros y literales codificados sin sacrificar el rendimiento o la seguridad de tipos.
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 expresión constante
Un objeto de expresión constante es un objeto declarado constexpr. Debe inicializarse con una expresión constante o un valor r construido por un constructor de expresión constante con argumentos de expresión constante.
Un objeto de expresión constante se comporta como si fuera declarado const, excepto que requiere inicialización antes de su uso y su inicializador debe ser una expresión constante. En consecuencia, un objeto de expresión constante siempre se puede usar como parte de otra expresión 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
Constructores de expresión constante
Un constructor de expresión constante es un constructor declarado constexpr. Puede tener una lista de inicialización de miembros, pero su cuerpo debe estar vacío, aparte de typedefs y estáticos. Sus argumentos deben tener tipos literales.
Un constructor de expresión constante permite al compilador inicializar el objeto en tiempo de compilación, siempre que los argumentos del constructor sean expresiones 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
Consejos del libro Effective Modern C ++ de Scott Meyers sobre constexpr:
constexpr los objetos son constantes y se inicializan con valores conocidos durante la compilación;
constexpr las funciones producen resultados en tiempo de compilación cuando se llaman con argumentos cuyos valores se conocen durante la compilación;
constexprlos objetos y funciones pueden usarse en una gama más amplia de contextos que los no constexprobjetos y funciones;
constexpr es parte de la interfaz de un objeto o función.
Gracias por el gran código de ejemplo que muestra las diferentes situaciones. Por grandiosas que sean algunas de las otras explicaciones, encontré que ver el código en acción es mucho más útil y comprensible. Realmente ayudó a solidificar mi comprensión de lo que está sucediendo.
RTHarston
35
De acuerdo con el libro de "The C ++ Programming Language 4th Editon" de Bjarne Stroustrup
• const : significa aproximadamente "Prometo no cambiar este valor" (§7.5). Esto se usa principalmente para especificar interfaces, de modo que los datos se puedan pasar a funciones sin temor a que se modifiquen.
El compilador hace cumplir la promesa hecha por const.
• constexpr : significa aproximadamente '' para ser evaluado en tiempo de compilación '' (§10.4). Esto se usa principalmente para especificar constantes, para permitir
Por ejemplo:
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 una función sea utilizable en una expresión constante, es decir, en una expresión que será evaluada por el compilador, debe definirse constexpr . Por ejemplo:
constexprdouble square(double x){return x∗x;}
Para ser constexpr, una función debe ser bastante simple: solo una declaración de retorno que computa un valor. Se puede usar una función constexpr para argumentos no constantes, pero cuando se hace esto, el resultado no es una expresión constante. Permitimos que se llame a una función constexpr con argumentos de expresión no constante en contextos que no requieren expresiones constantes, de modo que no tenemos que definir esencialmente la misma función dos veces: una para expresiones constantes y otra para variables.
En algunos lugares, las reglas del lenguaje requieren expresiones constantes (por ejemplo, límites de matriz (§2.2.5, §7.3), etiquetas de caso (§2.2.4, §9.4.2), algunos argumentos de plantilla (§25.2) y constantes declaradas usando constexpr). En otros casos, la evaluación en tiempo de compilación es importante para el rendimiento. Independientemente de los problemas de rendimiento, la noción de inmutabilidad (de un objeto con un estado inmutable) es una preocupación importante del diseño (§10.4).
Todavía hay problemas de rendimiento. Parece que la función constexpr si se evalúa en tiempo de ejecución puede ser más lenta que la versión de la función no constexpr. Además, si tenemos un valor constante, ¿deberíamos preferir "const" o "constexpr"? (más un conjunto generado por una pregunta de estilo se ve igual)
CoffeDeveloper
31
Ambos consty constexprse pueden aplicar a variables y funciones. Aunque son similares entre sí, de hecho son conceptos muy diferentes.
Tanto consty constexprmedia que sus valores no se pueden cambiar después de su inicialización. Así por ejemplo:
constint x1=10;constexprint x2=10;
x1=20;// ERROR. Variable 'x1' can't be changed.
x2=20;// ERROR. Variable 'x2' can't be changed.
La principal diferencia entre consty constexpres el momento en que se conocen (evalúan) sus valores de inicialización. Si bien los valores de las constvariables se pueden evaluar tanto en tiempo de compilación como en tiempo de ejecución, constexprsiempre se evalúan en tiempo de compilación. Por ejemplo:
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.
La ventaja clave de saber si el valor se conoce en tiempo de compilación o en tiempo de ejecución es el hecho de que las constantes de tiempo de compilación se pueden usar siempre que se necesiten constantes de tiempo de compilación. Por ejemplo, C ++ no le permite especificar matrices C con longitudes variables.
int temp=rand();// temp is generated by the the random generator at runtime.int array1[10];// OK.int array2[temp];// ERROR.
Entonces 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.
Por lo tanto, las constvariables pueden definir tanto constantes de tiempo de compilación como size1esa que se pueden usar para especificar tamaños de matriz y constantes de tiempo de ejecución como size2esas solo se conocen en tiempo de ejecución y no se pueden usar para definir tamaños de matriz. Por otro lado, constexprsiempre defina constantes de tiempo de compilación que puedan especificar tamaños de matriz.
Ambos consty constexprse pueden aplicar a funciones también. Una constfunción debe ser una función miembro (método, operador) donde la aplicación de la constpalabra clave significa que el método no puede cambiar los valores de sus campos miembros (no estáticos). Por ejemplo.
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 constexpres un concepto diferente. Marca una función (miembro o no miembro) como la función que se puede evaluar en tiempo de compilación si las constantes de tiempo de compilación se pasan como argumentos . Por ejemplo, puedes escribir esto.
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.
Por cierto, las constexprfunciones son las funciones normales de C ++ a las que se puede llamar incluso si se pasan argumentos no constantes. Pero en ese caso está obteniendo los valores no 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.
También constexprse puede aplicar a las funciones miembro (métodos), operadores e incluso constructores. Por ejemplo.
class test2
{staticconstexprint function(int value){return(value+1);}void f(){int x[function(10)];}};
Una muestra más 'loca'.
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.
Además, en C, constexpr intexiste pero se deletreaconst int
curioso el
8
Como @ 0x499602d2 ya señaló, constsolo asegura que un valor no se pueda cambiar después de la inicialización, donde como constexpr(introducido en C ++ 11) garantiza que la variable es una constante de tiempo de compilación.
Considere el siguiente ejemplo (de 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 varse puede establecer dinámicamente en un valor en tiempo de ejecución y una vez que se establece en ese valor, ya no se puede cambiar.
A constexpr int varno se puede establecer dinámicamente en tiempo de ejecución, sino en tiempo de compilación. Y una vez que se establece en ese valor, ya no se puede cambiar.
Aquí hay un ejemplo 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}
El fragmento anterior se compila bien y he comentado los que causan errores.
Las nociones clave aquí para tener en cuenta, son las nociones de compile timey run time. Se han introducido nuevas innovaciones en C ++ destinadas al máximo a ** know **ciertas cosas en el momento de la compilación para mejorar el rendimiento en tiempo de ejecución.
No creo que ninguna de las respuestas realmente deje en claro exactamente qué efectos secundarios tiene, o de hecho, cuál es.
constexpry consten el espacio de nombres / alcance del archivo son idénticos cuando se inicializan con un literal o una expresión; pero con una función, constpuede ser inicializada por cualquier función, pero constexprinicializada por un no constexpr (una función que no está marcada con constexpr o una expresión no constexpr) generará un error del compilador. Ambos constexpry constson implícitamente enlaces internos para variables (bueno, en realidad, no sobreviven para llegar a la etapa de enlace si compilan -O1 y son más fuertes, y staticno obligan al compilador a emitir un símbolo de enlace interno (local) para consto constexprcuando -O1 o más fuerte; la única vez que lo hace es si toma la dirección de la variable consty constexprserá un símbolo interno a menos que se exprese conextern ieextern constexpr/const int i = 3;necesita ser utilizado). En una función, constexprhace que la función nunca alcance permanentemente la etapa de enlace (independientemente de externo inlineen la definición o -O0 o -Ofast), mientras que constnunca lo hace, staticy inlinesolo tiene este efecto en -O1 y superior. Cuando una función inicializa una variable const/ , la carga siempre se optimiza con cualquier indicador de optimización, pero nunca se optimiza si la función es solo o , o si la variable no es / .constexprconstexprstaticinlineconstconstexpr
Compilación estándar (-O0)
#include<iostream>constexprint multiply (int x,int y){return x * y;}externconstint val = multiply(10,10);int main (){
std::cout << val;}
compila a
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):...
sin embargo
#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 a
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
Esto muestra claramente que constexprhace que la inicialización de la const/constexprvariable de alcance del archivo ocurra en tiempo de compilación y no produzca ningún símbolo global, mientras que no usarla hace que la inicialización ocurra antes mainen tiempo de ejecución.
constexprLas funciones también se pueden llamar desde otras constexprfunciones para obtener el mismo resultado. constexpren una función también evita el uso de cualquier cosa que no se pueda hacer en tiempo de compilación en la función; por ejemplo, una llamada al <<operador en std::cout.
constexpren el alcance del bloque se comporta de la misma manera que produce un error si se inicializa por una función no constexpr; el valor también se sustituye de inmediato.
Al final, su propósito principal es como la función en línea de C, pero solo es efectiva cuando la función se usa para inicializar variables de alcance de archivo (que las funciones no pueden hacer en C, pero pueden hacerlo en C ++ porque permite la inicialización dinámica del archivo) variables de alcance), excepto que la función no puede exportar un símbolo global / local al enlazador también, incluso usando extern/static, lo que podría hacer con inlineC; las funciones de asignación de variables de alcance de bloque pueden integrarse simplemente usando una optimización -O1 sin constexprC y C ++.
Buen punto en el enlazador. ¿Podría considerarse más seguro en general usar constexpr ya que da como resultado menos fugas de símbolos?
Neil McGill
1
@NeilMcGill no realmente porque inline y static harán que el compilador no emita un símbolo local para multiplicar si compila usando -O1 o más fuerte. Constexpr es el único que optimiza la carga para val, pero aparte de eso, es idéntico a poner estática o en línea antes de la función. Olvidé algo más también. Constexpr es la única palabra clave que no emite un símbolo para la función en -O0, static y en línea do
Lewis Kelsey
1
En primer lugar, ambos son calificadores en c ++. Una variable declarada const debe inicializarse y no puede cambiarse en el futuro. Por lo tanto, generalmente una variable declarada como constante tendrá un valor incluso antes de compilar.
Pero, para constexpr, es un poco diferente.
Para constexpr, puede dar una expresión que podría evaluarse durante la compilación del programa.
Obviamente, la variable declarada como constexper no se puede cambiar en el futuro al igual que const.
constexpr
crea una constante de tiempo de compilación;const
simplemente significa que el valor no se puede cambiar.boost/hana
biblioteca pueda aclarar algunosconstexpr
problemas donde puede usarconstexpr
y donde no puede: boost.org/doc/libs/1_69_0/libs/hana/doc/html/…constexpr
aquel entonces :)Respuestas:
Significado básico y sintaxis
Ambas palabras clave se pueden utilizar en la declaración de objetos y funciones. La diferencia básica cuando se aplica a los objetos es esta:
const
declara un objeto como constante . Esto implica una garantía de que una vez inicializado, el valor de ese objeto no cambiará, y el compilador puede hacer uso de este hecho para optimizaciones. También ayuda a evitar que el programador escriba código que modifique objetos que no debían modificarse después de la inicialización.constexpr
declara un objeto apto para su uso en lo que el estándar llama expresiones constantes . Pero tenga en cuenta queconstexpr
no es la única forma de hacer esto.Cuando se aplica a las funciones, la diferencia básica es esta:
const
solo se puede usar para funciones miembro no estáticas, no funciones en general. Da una garantía de que la función miembro no modifica ninguno de los miembros de datos no estáticos.constexpr
se puede usar con funciones miembro y no miembro, así como con constructores. Declara la función apta para su uso en expresiones constantes . El compilador solo lo aceptará si la función cumple con ciertos criterios (7.1.5 / 3,4), lo más importante (†) :return
afirmaciones estáticas, solo se permite una sola declaración. En el caso de un constructor, solo se permiten una lista de inicialización, typedefs y aserción estática. (= default
y= delete
están permitidos).asm
declaración, unagoto
declaración, una declaración con una etiqueta que no seacase
ydefault
, try-block, la definición de una variable no literal tipo, definición de una variable de duración de almacenamiento estático o de subprocesos, la definición de una variable para la que no se realiza ninguna inicialización.Expresiones constantes
Como se dijo anteriormente,
constexpr
declara que tanto los objetos como las funciones son aptos para su uso en expresiones constantes. Una expresión constante es más que simplemente constante:Se puede usar en lugares que requieren evaluación en tiempo de compilación, por ejemplo, parámetros de plantilla y especificadores de tamaño de matriz:
Pero nota:
Declarar algo como
constexpr
no necesariamente garantiza que se evaluará en tiempo de compilación. Se puede usar para eso, pero también se puede usar en otros lugares que se evalúan en tiempo de ejecución.Un objeto puede ser apto para su uso en expresiones constantes sin ser declarado
constexpr
. Ejemplo:Esto es posible porque
N
, al ser constante e inicializado en el momento de la declaración con un literal, satisface los criterios para una expresión constante, incluso si no se declaraconstexpr
.Entonces, ¿cuándo debo usarlo
constexpr
?Un objeto como el
N
anterior se puede usar como expresión constante sin ser declaradoconstexpr
. Esto es cierto para todos los objetos que son:const
[Esto se debe a §5.19 / 2: una expresión constante no debe incluir una subexpresión que implique "una modificación de valor a valor a menos que [...] un valor de tipo integral o enumeración [...]" Gracias a Richard Smith por corregir mi afirmación anterior de que esto era cierto para todos los tipos literales.]
Para que una función sea apta para su uso en expresiones constantes, debe declararse explícitamente
constexpr
; no es suficiente simplemente para satisfacer los criterios para las funciones de expresión constante. Ejemplo:¿Cuándo puedo / debo usar ambos
const
yconstexpr
juntos?A. En declaraciones de objeto. Esto nunca es necesario cuando ambas palabras clave se refieren al mismo objeto a declarar.
constexpr
implicaconst
.es lo mismo que
Sin embargo, tenga en cuenta que puede haber situaciones en las que las palabras clave se refieren a diferentes partes de la declaración:
Aquí,
NP
se declara como una dirección de expresión constante, es decir, un puntero que es en sí mismo una expresión constante. (Esto es posible cuando la dirección se genera aplicando el operador de dirección a una expresión constante estática / global). Aquí, ambosconstexpr
yconst
son obligatorios:constexpr
siempre se refiere a la expresión que se declara (aquíNP
), mientras que seconst
refiere aint
(declara un puntero- a-const). La eliminación deconst
haría que la expresión sea ilegal (porque (a) un puntero a un objeto no constante no puede ser una expresión constante, y (b)&N
es, de hecho, un puntero a constante).B. En declaraciones de funciones miembro. En C ++ 11,
constexpr
implicaconst
, mientras que en C ++ 14 y C ++ 17 ese no es el caso. Una función miembro declarada bajo C ++ 11 comonecesita ser declarado como
bajo C ++ 14 para poder seguir siendo utilizable como una
const
función.fuente
constexpr
función en una expresión no constante, por ejemplo, una variable ordinaria, esto es perfectamente legal y la función se utilizará como cualquier otra función. No se evaluará en tiempo de compilación (porque no puede). Tal vez piense que es obvio, pero si afirmo que una función declarada comoconstexpr
siempre se evaluará en tiempo de compilación, podría interpretarse de manera incorrecta.constexpr
objetos, no de funciones. Me gusta pensar que losconstexpr
objetos fuerzan la evaluación de valores en tiempo de compilación, y que lasconstexpr
funciones permiten evaluar la función en tiempo de compilación o en tiempo de ejecución según corresponda.mutable
también pueden ser modificados por lasconst
funciones del miembro.const
aplica para las variables y evita que se modifiquen en su código.constexpr
le dice al compilador que esta expresión da como resultado un valor constante de tiempo de compilación , por lo que puede usarse en lugares como longitudes de matriz, asignación aconst
variables, etc. El enlace proporcionado por Oli tiene muchos ejemplos excelentes.Básicamente son 2 conceptos diferentes en total, y pueden (y deberían) usarse juntos.
fuente
Visión general
const
garantiza que un programa no cambia el valor de un objeto . Sin embargo,const
no garantiza a qué tipo de inicialización se somete el objeto.Considerar:
La función
max()
simplemente devuelve un valor literal. Sin embargo, debido a que el inicializador es una llamada de función, semx
somete a una inicialización de tiempo de ejecución. Por lo tanto, no puede usarlo como una expresión constante :constexpr
es una nueva palabra clave de C ++ 11 que elimina la necesidad de crear macros y literales codificados. También garantiza, bajo ciertas condiciones, que los objetos se someten a una inicialización estática . Controla el tiempo de evaluación de una expresión. Al aplicar la evaluación de su expresión en tiempo de compilación , leconstexpr
permite definir expresiones constantes verdaderas que son cruciales para aplicaciones críticas de tiempo, programación del sistema, plantillas y, en general, en cualquier código que se base en constantes de tiempo de compilación.Funciones de expresión constante
Una función de expresión constante es una función declarada
constexpr
. Su cuerpo debe ser no virtual y consistir en una sola declaración de retorno, además de typedefs y aserciones estáticas. Sus argumentos y valor de retorno deben tener tipos literales. Se puede usar con argumentos de expresión no constante, pero cuando eso se hace, el resultado no es una expresión constante.Una función de expresión constante está destinada a reemplazar macros y literales codificados sin sacrificar el rendimiento o la seguridad de tipos.
Objetos de expresión constante
Un objeto de expresión constante es un objeto declarado
constexpr
. Debe inicializarse con una expresión constante o un valor r construido por un constructor de expresión constante con argumentos de expresión constante.Un objeto de expresión constante se comporta como si fuera declarado
const
, excepto que requiere inicialización antes de su uso y su inicializador debe ser una expresión constante. En consecuencia, un objeto de expresión constante siempre se puede usar como parte de otra expresión constante.Constructores de expresión constante
Un constructor de expresión constante es un constructor declarado
constexpr
. Puede tener una lista de inicialización de miembros, pero su cuerpo debe estar vacío, aparte de typedefs y estáticos. Sus argumentos deben tener tipos literales.Un constructor de expresión constante permite al compilador inicializar el objeto en tiempo de compilación, siempre que los argumentos del constructor sean expresiones constantes.
Consejos del libro Effective Modern C ++ de Scott Meyers sobre
constexpr
:constexpr
los objetos son constantes y se inicializan con valores conocidos durante la compilación;constexpr
las funciones producen resultados en tiempo de compilación cuando se llaman con argumentos cuyos valores se conocen durante la compilación;constexpr
los objetos y funciones pueden usarse en una gama más amplia de contextos que los noconstexpr
objetos y funciones;constexpr
es parte de la interfaz de un objeto o función.Fuente: Uso de constexpr para mejorar la seguridad, el rendimiento y la encapsulación en C ++ .
fuente
De acuerdo con el libro de "The C ++ Programming Language 4th Editon" de Bjarne Stroustrup
• const : significa aproximadamente "Prometo no cambiar este valor" (§7.5). Esto se usa principalmente para especificar interfaces, de modo que los datos se puedan pasar a funciones sin temor a que se modifiquen.
El compilador hace cumplir la promesa hecha por const.
• constexpr : significa aproximadamente '' para ser evaluado en tiempo de compilación '' (§10.4). Esto se usa principalmente para especificar constantes, para permitir
Por ejemplo:
Para que una función sea utilizable en una expresión constante, es decir, en una expresión que será evaluada por el compilador, debe definirse constexpr .
Por ejemplo:
Para ser constexpr, una función debe ser bastante simple: solo una declaración de retorno que computa un valor. Se puede usar una función constexpr para argumentos no constantes, pero cuando se hace esto, el resultado no es una expresión constante. Permitimos que se llame a una función constexpr con argumentos de expresión no constante en contextos que no requieren expresiones constantes, de modo que no tenemos que definir esencialmente la misma función dos veces: una para expresiones constantes y otra para variables.
En algunos lugares, las reglas del lenguaje requieren expresiones constantes (por ejemplo, límites de matriz (§2.2.5, §7.3), etiquetas de caso (§2.2.4, §9.4.2), algunos argumentos de plantilla (§25.2) y constantes declaradas usando constexpr). En otros casos, la evaluación en tiempo de compilación es importante para el rendimiento. Independientemente de los problemas de rendimiento, la noción de inmutabilidad (de un objeto con un estado inmutable) es una preocupación importante del diseño (§10.4).
fuente
Ambos
const
yconstexpr
se pueden aplicar a variables y funciones. Aunque son similares entre sí, de hecho son conceptos muy diferentes.Tanto
const
yconstexpr
media que sus valores no se pueden cambiar después de su inicialización. Así por ejemplo:La principal diferencia entre
const
yconstexpr
es el momento en que se conocen (evalúan) sus valores de inicialización. Si bien los valores de lasconst
variables se pueden evaluar tanto en tiempo de compilación como en tiempo de ejecución,constexpr
siempre se evalúan en tiempo de compilación. Por ejemplo:La ventaja clave de saber si el valor se conoce en tiempo de compilación o en tiempo de ejecución es el hecho de que las constantes de tiempo de compilación se pueden usar siempre que se necesiten constantes de tiempo de compilación. Por ejemplo, C ++ no le permite especificar matrices C con longitudes variables.
Entonces significa que:
Por lo tanto, las
const
variables pueden definir tanto constantes de tiempo de compilación comosize1
esa que se pueden usar para especificar tamaños de matriz y constantes de tiempo de ejecución comosize2
esas solo se conocen en tiempo de ejecución y no se pueden usar para definir tamaños de matriz. Por otro lado,constexpr
siempre defina constantes de tiempo de compilación que puedan especificar tamaños de matriz.Ambos
const
yconstexpr
se pueden aplicar a funciones también. Unaconst
función debe ser una función miembro (método, operador) donde la aplicación de laconst
palabra clave significa que el método no puede cambiar los valores de sus campos miembros (no estáticos). Por ejemplo.A
constexpr
es un concepto diferente. Marca una función (miembro o no miembro) como la función que se puede evaluar en tiempo de compilación si las constantes de tiempo de compilación se pasan como argumentos . Por ejemplo, puedes escribir esto.Por cierto, las
constexpr
funciones son las funciones normales de C ++ a las que se puede llamar incluso si se pasan argumentos no constantes. Pero en ese caso está obteniendo los valores no constexpr.También
constexpr
se puede aplicar a las funciones miembro (métodos), operadores e incluso constructores. Por ejemplo.Una muestra más 'loca'.
fuente
constexpr int
existe pero se deletreaconst int
Como @ 0x499602d2 ya señaló,
const
solo asegura que un valor no se pueda cambiar después de la inicialización, donde comoconstexpr
(introducido en C ++ 11) garantiza que la variable es una constante de tiempo de compilación.Considere el siguiente ejemplo (de LearnCpp.com):
fuente
A
const int var
se puede establecer dinámicamente en un valor en tiempo de ejecución y una vez que se establece en ese valor, ya no se puede cambiar.A
constexpr int var
no se puede establecer dinámicamente en tiempo de ejecución, sino en tiempo de compilación. Y una vez que se establece en ese valor, ya no se puede cambiar.Aquí hay un ejemplo sólido:
El fragmento anterior se compila bien y he comentado los que causan errores.
Las nociones clave aquí para tener en cuenta, son las nociones de
compile time
yrun time
. Se han introducido nuevas innovaciones en C ++ destinadas al máximo a** know **
ciertas cosas en el momento de la compilación para mejorar el rendimiento en tiempo de ejecución.fuente
No creo que ninguna de las respuestas realmente deje en claro exactamente qué efectos secundarios tiene, o de hecho, cuál es.
constexpr
yconst
en el espacio de nombres / alcance del archivo son idénticos cuando se inicializan con un literal o una expresión; pero con una función,const
puede ser inicializada por cualquier función, peroconstexpr
inicializada por un no constexpr (una función que no está marcada con constexpr o una expresión no constexpr) generará un error del compilador. Ambosconstexpr
yconst
son implícitamente enlaces internos para variables (bueno, en realidad, no sobreviven para llegar a la etapa de enlace si compilan -O1 y son más fuertes, ystatic
no obligan al compilador a emitir un símbolo de enlace interno (local) paraconst
oconstexpr
cuando -O1 o más fuerte; la única vez que lo hace es si toma la dirección de la variableconst
yconstexpr
será un símbolo interno a menos que se exprese conextern
ieextern constexpr/const int i = 3;
necesita ser utilizado). En una función,constexpr
hace que la función nunca alcance permanentemente la etapa de enlace (independientemente deextern
oinline
en la definición o -O0 o -Ofast), mientras queconst
nunca lo hace,static
yinline
solo tiene este efecto en -O1 y superior. Cuando una función inicializa una variableconst
/ , la carga siempre se optimiza con cualquier indicador de optimización, pero nunca se optimiza si la función es solo o , o si la variable no es / .constexpr
constexpr
static
inline
const
constexpr
Compilación estándar (-O0)
compila a
sin embargo
Compila a
Esto muestra claramente que
constexpr
hace que la inicialización de laconst/constexpr
variable de alcance del archivo ocurra en tiempo de compilación y no produzca ningún símbolo global, mientras que no usarla hace que la inicialización ocurra antesmain
en tiempo de ejecución.Compilando usando -Ofast
¡Incluso -Ofast no optimiza la carga! https://godbolt.org/z/r-mhif , por lo que necesita
constexpr
constexpr
Las funciones también se pueden llamar desde otrasconstexpr
funciones para obtener el mismo resultado.constexpr
en una función también evita el uso de cualquier cosa que no se pueda hacer en tiempo de compilación en la función; por ejemplo, una llamada al<<
operador enstd::cout
.constexpr
en el alcance del bloque se comporta de la misma manera que produce un error si se inicializa por una función no constexpr; el valor también se sustituye de inmediato.Al final, su propósito principal es como la función en línea de C, pero solo es efectiva cuando la función se usa para inicializar variables de alcance de archivo (que las funciones no pueden hacer en C, pero pueden hacerlo en C ++ porque permite la inicialización dinámica del archivo) variables de alcance), excepto que la función no puede exportar un símbolo global / local al enlazador también, incluso usando
extern/static
, lo que podría hacer coninline
C; las funciones de asignación de variables de alcance de bloque pueden integrarse simplemente usando una optimización -O1 sinconstexpr
C y C ++.fuente
En primer lugar, ambos son calificadores en c ++. Una variable declarada const debe inicializarse y no puede cambiarse en el futuro. Por lo tanto, generalmente una variable declarada como constante tendrá un valor incluso antes de compilar.
Pero, para constexpr, es un poco diferente.
Para constexpr, puede dar una expresión que podría evaluarse durante la compilación del programa.
Obviamente, la variable declarada como constexper no se puede cambiar en el futuro al igual que const.
fuente