Diferencia entre `constexpr` y` const`

593

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
fuente
71
constexprcrea una constante de tiempo de compilación; constsimplemente significa que el valor no se puede cambiar.
0x499602D2
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 expression
    
    int 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()
      {
        const int 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>
    class list
    { };
    
    constexpr int sqr1(int arg)
    { return arg * arg; }
    
    int sqr2(int arg)
    { return arg * arg; }
    
    int main()
    {
      const int X = 2;
      list<sqr1(X)> mylist1;  // OK: sqr1 is constexpr
      list<sqr2(X)> mylist2;  // wrong: sqr2 is not constexpr
    }

¿Cuándo puedo / debo usar ambos consty constexpr juntos?

A. En declaraciones de objeto. Esto nunca es necesario cuando ambas palabras clave se refieren al mismo objeto a declarar. constexprimplica const.

constexpr const int N = 5;

es lo mismo que

constexpr int 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:

static constexpr int N = 3;

int main()
{
  constexpr const int *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

constexpr void f();

necesita ser declarado como

constexpr void f() const;

bajo C ++ 14 para poder seguir siendo utilizable como una constfunción.

jogojapan
fuente
3
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.

Karthik T
fuente
2
uso de const y constexpr, ej .: en.cppreference.com/w/cpp/container/array/get
Manohar Reddy Poreddy
64

Visión general

  • constgarantiza 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:

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

    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.

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

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

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

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

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.

Fuente: Uso de constexpr para mejorar la seguridad, el rendimiento y la encapsulación en C ++ .

zangw
fuente
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:

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

Para que 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:

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


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).

Mustafa Ekici
fuente
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:

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 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.

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 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:

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.

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.

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, 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.
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.

También constexprse 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.
Timmy_A
fuente
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;

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

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[]) {
    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 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.

truthadjustr
fuente
3

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>
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 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.

Compilando usando -Ofast

¡Incluso -Ofast no optimiza la carga! https://godbolt.org/z/r-mhif , por lo que necesita constexpr


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 ++.

Lewis Kelsey
fuente
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.

Subhash Malireddy
fuente