Cuál es la diferencia entre constexpr
y const
?
- ¿Cuándo puedo usar solo uno de ellos?
- ¿Cuándo puedo usar ambos y cómo debo elegir uno?
MBZ
Cuál es la diferencia entre constexpr
y const
?
Karthik T
const
aplica para Variablesy evita que se modifiquen en tu código.
constexpr
le dice al compilador que esto expresión resulta en un valor constante de tiempo de compilaciónpor lo que se puede usar en lugares como longitudes de matrices, asignando a const
variables, etc. El enlace proporcionado por Oli tiene muchos ejemplos excelentes.
Básicamente, son 2 conceptos diferentes en total, y pueden (y deben) usarse juntos.
uso de const y constexpr, por ejemplo: es.cppreference.com/w/cpp/container/array/get
– Manohar Reddy Poreddy
14 de mayo de 2017 a las 6:13
@ManoharReddyPoreddy creo es.cppreference.com/w/cpp/container/array/begin es un mejor ejemplo, tiene firmas con constexpr T f(x) const
donde ambos se aplican a la función, mientras que en constexpr const T f(x)
(es decir, la matriz::obtener firma) la const
es parte del tipo de devolución en lugar de la propiedad de la función (no estoy seguro del nombre para esto en estándar). Aunque este anunciante no reconoce el uso de const
sobre las funciones de los miembros.
– Ted
28 de diciembre de 2020 a las 22:49
@ted Cambié mi lenguaje de programación de C ++ a javascript desde hace algún tiempo, por lo que apenas recuerdo lo publicado anteriormente :), y por lo tanto no puedo comentar por la misma razón.
– Manohar Reddy Poreddy
29 de diciembre de 2020 a las 14:17
Gracias por el excelente código de ejemplo que muestra las diferentes situaciones. Por muy buenas 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á pasando.
– RTHarston
9 de febrero de 2019 a las 8:56
Gracias, esto me ayudó a entender mejor que la respuesta seleccionada.
– AndreiToroplean
8 de noviembre de 2020 a las 11:49
Timmy_A
Ambas cosas const
y constexpr
se puede aplicar a variables y funciones. Aunque son similares entre sí, en realidad son conceptos muy diferentes.
Ambas cosas const
y constexpr
significa que sus valores no se pueden cambiar después de su inicialización. Así por ejemplo:
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.
La principal diferencia entre const
y constexpr
es el momento en que se conocen (evaluan) sus valores de inicialización. Mientras que los valores de const
las 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:
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.
La ventaja clave para 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:
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.
Asi que const
las variables pueden definir ambos compilar constantes de tiempo me gusta size1
que se puede utilizar para especificar tamaños de matriz y constantes de tiempo de ejecución me gusta size2
que solo se conocen en tiempo de ejecución y no se pueden usar para definir tamaños de matrices. Por otro lado constexpr
Siempre defina constantes de tiempo de compilación que puedan especificar tamaños de matriz.
Ambas cosas const
y constexpr
también se puede aplicar a funciones. UN const
función debe ser una función miembro (método, operador) donde la aplicación de const
palabra 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.
}
};
UN 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 sus argumentos. Por ejemplo, puedes escribir esto.
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.
por cierto el constexpr
Las funciones son las funciones regulares de C++ que se pueden llamar incluso si se pasan argumentos no constantes. Pero en ese caso, obtendrá los valores no 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.
Él constexpr
también se puede aplicar a las funciones miembro (métodos), operadores e incluso constructores. Por ejemplo.
class test2
{
static constexpr int 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.
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.
Además, en C, constexpr int
existe pero se escribe const int
– chico curioso
18 de enero de 2019 a las 22:22
No creo que ninguna de las respuestas aclare exactamente qué efectos secundarios tiene o, de hecho, qué es.
constexpr
y const
at namespace/file-scope son idénticos cuando se inicializan con un literal o una expresión; pero con una función, const
puede ser inicializado por cualquier función, pero constexpr
inicializado por una expresión no constexpr (una función que no está marcada con constexpr o una expresión no constexpr) generará un error de compilación. Ambas cosas constexpr
y const
son enlaces implícitamente internos para las variables (bueno, en realidad, no sobreviven para llegar a la etapa de enlace si se compila -O1 y más fuerte, y static
no obliga al compilador a emitir un símbolo de enlace interno (local) para const
o constexpr
cuando está en -O1 o más fuerte; la única vez que hace esto es si toma la dirección de la variable. const
y constexpr
será un símbolo interno a menos que se exprese con extern
es decir extern constexpr/const int i = 3;
necesita ser usado). En una función, constexpr
hace que la función de forma permanente nunca llegue a la etapa de vinculación (independientemente de extern
o inline
en la definición o -O0 o -Ofast), mientras que const
nunca lo hace, y static
y inline
solo tiene este efecto en -O1 y superiores. Cuando un const
/constexpr
variable es inicializada por un constexpr
función, la carga siempre se optimiza con cualquier bandera de optimización, pero nunca se optimiza si la función es solo static
o inline
o si la variable no es una const
/constexpr
.
Compilación estándar (-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 a
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):
.
.
.
Sin embargo
#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 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 constexpr
provoca la inicialización del const/constexpr
La variable de alcance de archivo se produce en el momento de la compilación y no produce ningún símbolo global, mientras que no usarla hace que la inicialización se produzca antes main
en tiempo de ejecución.
Compilando usando -Ofast
¡Incluso -Ofast no optimiza la carga! https://godbolt.org/z/r-mhifvos tambien necesidad constexpr
constexpr
Las funciones también se pueden llamar desde dentro de otros constexpr
funciones para el mismo resultado. constexpr
on a function 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
.
constexpr
en el alcance del bloque se comporta igual en el sentido de que produce un error si se inicializa mediante una función que no es constexpr; el valor también se sustituye inmediatamente.
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 ámbito de archivo (que las funciones no pueden hacer en C, pero sí en C++ porque permite la inicialización dinámica de archivos). variables de alcance), excepto que la función tampoco puede exportar un símbolo global/local al enlazador, incluso usando extern/static
con el que podrías inline
en C; Las funciones de asignación de variables de alcance de bloque se pueden incorporar simplemente usando una optimización -O1 sin constexpr
en C y C++.
Además, en C, constexpr int
existe pero se escribe const int
– chico curioso
18 de enero de 2019 a las 22:22
UN 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.
UN constexpr int var
no se puede configurar 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[]) {
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
}
El fragmento de código anterior compila bien y he comentado aquellos que causan el error.
Las nociones clave aquí para tomar nota son las nociones de compile time
y run time
. Se han introducido nuevas innovaciones en C++ con la intención de, en la medida de lo posible, ** know **
ciertas cosas en tiempo de compilación para mejorar el rendimiento en tiempo de ejecución.
Cualquier intento de explicación que no involucre las dos nociones clave anteriores es una alucinación.
constexpr
crea una constante de tiempo de compilación;const
simplemente significa que el valor no se puede cambiar.– David G.
2 de enero de 2013 a las 1:44
Consulte también ¿Cuándo debería usar la capacidad constexpr en C++ 11?
– jww
3 de septiembre de 2016 a las 3:53
Puede ser este artículo de
boost/hana
la biblioteca puede iluminar algunosconstexpr
problemas en los que puede utilizarconstexpr
y donde no puedes: boost.org/doc/libs/1_69_0/libs/hana/doc/html/…– andry
7 de febrero de 2019 a las 8:38
@0x499602D2 “simplemente significa que el valor no se puede cambiar” Para un escalar inicializado con un literal, un valor que no se puede cambiar es también una constante de tiempo de compilación.
– chico curioso
20 de enero de 2020 a las 23:09
@curiousguy Sí, mi comentario fue muy simplificado. Es cierto que era nuevo en
constexpr
entonces tambien 🙂– David G.
20 de enero de 2020 a las 23:12