No creo que ninguna de las respuestas realmente deje en claro exactamente qué efectos secundarios tiene, o de hecho, cuál es.
constexpr
y const
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, pero constexpr
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. Ambos constexpr
y const
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, y static
no obligan al compilador a emitir un símbolo de enlace interno (local) para const
o constexpr
cuando -O1 o más fuerte; la única vez que lo hace es si toma la dirección de la variable const
y constexpr
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 de extern
o inline
en la definición o -O0 o -Ofast), mientras que const
nunca lo hace, static
y inline
solo 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 / .constexpr
constexpr
static
inline
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
hace que la inicialización de la const/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 antes main
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 otras constexpr
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 en std::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 con inline
C; las funciones de asignación de variables de alcance de bloque pueden integrarse simplemente usando una optimización -O1 sin constexpr
C y C ++.
constexpr
crea una constante de tiempo de compilación;const
simplemente significa que el valor no se puede cambiar.