c++: ¿Cuál es la diferencia entre constexpr y const?

CorePress2024-01-24  12

¿Cuál es la diferencia entre constexpr y const?

¿Cuándo puedo utilizar sólo uno de ellos? ¿Cuándo puedo usar ambos y cómo debo elegir uno?

135

constexpr crea una constante en 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

1

Consulte también ¿Cuándo debería utilizar la capacidad constexpr en C++11?

- jww

3 de septiembre de 2016 a las 3:53

Es posible que este artículo de la biblioteca boost/hana pueda aclarar algunos problemas de constexpr en los que puedes usar constexpr y en los que no: boost.org/doc/libs/1_69_0/libs/hana/doc/html /…

Andry

7 febrero 2019 a las 8:38

1

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

- chico curioso

20/01/2020 a las 23:09

@curiousguy Sí, mi comentario fue muy simplificado. Es cierto que yo también era nuevo en constexpr en aquel entonces :)

- David G.

20/01/2020 a las 23:12



------------------------------------

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 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 utilizar 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 que un objeto es apto para su uso en lo que el Estándar llama conexpresiones constantes. Pero tenga en cuenta que constexpr no es la única forma de hacerlo.

Cuando se aplica a funciones, la diferencia básica es esta:

const solo se puede utilizar para funciones miembro no estáticas, no para funciones en general. Ofrece una garantía de que la función miembro no modifica ninguno de los miembros de datos no estáticos (excepto los miembros de datos mutables, que se pueden modificar de todos modos).

constexpr se puede utilizar con funciones miembro y no miembro, así como con constructores. Declara la función apta para su uso en expresiones constantes. El compilador sólo lo aceptará si la función cumple ciertos criterios (7.1.5/3,4), el más importante (†):

El cuerpo de la función debe ser no virtual y extremadamente simple: aparte de los typedefs y las afirmaciones estáticas, solo se permite una única declaración de retorno.bajado. En el caso de un constructor, solo se permiten una lista de inicialización, typedefs y afirmaciones estáticas. (Sin embargo, = predeterminado y = eliminar también 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: declaración asm, una declaración goto, una declaración con una etiqueta distinta a case y default, try-block, la definición de una variable. de tipo no literal, definición de una variable de duración de almacenamiento estática o de subproceso, definición de una variable para la cual 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, normalmente escalares o agregados). Expresiones constantes

Como se dijo anteriormente, constexpr declara que tanto los objetos como las funciones son aptos para su uso en constante expr.sesiones. Una expresión constante es más que simplemente constante:

Se puede utilizar 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 constexpr no necesariamente garantiza que será evaluado en tiempo de compilación. Se puede utilizar para ello, pero también se puede utilizar 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 tengo que usar constexpr?

un objetoect como N arriba se puede usar como expresión constante sin ser declarado constexpr. Esto es cierto para todos los objetos que son: constante y 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 gl de tipo integral o de 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 que simplemente satisfaga los criterios para 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, const y constexpr juntos?

A. En declaraciones de objetos. Esto nunca es necesario cuando ambas palabras clave hacen referencia al mismo objeto a declarar. constexpr implica constante.

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 cada palabra clave haga referencia a diferentes partes de la declaración:

static constexpr int N = 3;

int main()
{
  constexpr const int *NP = &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í, se requieren tanto constexpr como const: constexpr siempre se refiere a la expresión que se declara (aquí NP), mientras que const se refiere a int (declara un puntero a constante). Eliminar la constante se desgarraríaUtilice la expresión como 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 una constante).

B. En declaraciones de funciones miembro. En C++11, constexpr implica const, mientras que en C++14 y C++17 ese no es el caso. Una función miembro declarada en C++ 11 como

constexpr void f();

debe ser declarado como

constexpr void f() const;

en C++ 14 para que aún pueda utilizarse como función constante.

Respondido al

2 de enero de 2013 a las 5:10

jogojapan

jogojapan

69k

11

11 insignias de oro

103

103 insignias de plata

132

132 insignias de bronce

29

14

En mi opinión, la opción "no necesariamente evaluada en el momento de la compilación" es menos útil que pensar en ellos como "evaluados en el momento de la compilación". Las restricciones sobre 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. Dado que no hay efectos secundarios, nunca se puede notar la diferencia entre un compilador "evaluó" hacerlo o no.

&norteguión; aschepler

2 de enero de 2013 a las 5:27

22

@aschepler Claro. Mi punto principal es que si llamas a una función constexpr en una expresión no constante, p. 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 dijera que una función declarada como constexpr siempre se evaluará en tiempo de compilación, podría interpretarse de manera incorrecta.

-jogojapan

2 de enero de 2013 a las 5:34

8

Sí, estaba hablando de objetos constexpr, no de funciones. Me gusta pensar que constexpr en objetos fuerza la evaluación de valores en tiempo de compilación, y que constexpr en funciones permite que la función se evalúe en tiempo de compilación o en tiempo de ejecución, según corresponda.

- aschepler

2 de enero de 2013 a las 5:38

5

Una corrección: 'const' es solo una restricciónque USTED no puede cambiar el valor de una variable; no hace ninguna promesa de que el valor no cambiará (es decir, por otra persona). Es una propiedad de escritura, no de lectura.

- Jared Grubb

20/01/2013 a las 17:05

4

Esta oración: Ofrece una garantía de que la función miembro no modifica ninguno de los miembros de datos no estáticos. pierde un detalle importante. Los miembros marcados como mutables también pueden ser modificados por funciones de miembros constantes.

- Omnifano

12 de marzo de 2018a las 7:44



------------------------------------

const se aplica a 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 matrices, asignación a variables constantes, etc. El enlace proporcionado por Oli tiene muchos ejemplos excelentes.

Básicamente son dos conceptos completamente diferentes y pueden (y deben) usarse juntos.

4

2

const & amp; Uso de constexpr, por ejemplo: en.cppreference.com/w/cpp/container/array/get

Manohar Reddy Poreddy

14 de mayo de 2017 a las 6:13

1

@ManoharReddyPoreddy Creo que en.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 constante es parte del tipo de retorno en lugar de than la propiedad de la función (no estoy seguro del nombre de esto en estándar). Aunque esta respuesta no reconoce el uso de const en funciones miembro.

-ted

28 dic 2020 a las 22:49

@ted Cambié mi lenguaje de programación de C++ a javascript desde hace algún tiempo, así que apenas recuerdo lo publicado arriba :), y por eso no puedo comentar por la misma razón.

Manohar Reddy Poreddy

29 de diciembre de 2020 en14:17

1

const no impide que se modifique una variable. Evita que el programador modifique la variable sin decir explícitamente que quiere hacerlo (mediante una conversión).

- einpoklum

28 de agosto de 2022 a las 13:56



------------------------------------

Descripción general

const garantiza que un programa no cambia el valor de un objeto. Sin embargo, const no garantiza qué tipo de inicializaciónsufre el objeto.

Considere:

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, mx se inicializa en tiempo de ejecución. Por lo tanto, no puedes utilizarlo como expresión constante:

int arr[mx];  // error: “constant expression required”

constexpr es una nueva palabra clave de C++11 que le elimina la necesidad de crear macros y literales codificados. También garantiza, bajo ciertas condiciones, que los objetos se sometan a una inicialización estática. Controla el tiempo de evaluación de una expresión. Al imponer la evaluación de su expresión en tiempo de compilación, constexpr le permite definir expresiones constantes verdaderas que son cruciales para aplicaciones en las que el tiempo es crítico, programación de sistemas, plantillas y, en general, en cualquier código que dependa de constantes en tiempo de compilación.

expresión constantenorte funciones

Una función de expresión constante es una función declarada constexpr. Su cuerpo debe ser no virtual y consistir únicamente en una única declaración de retorno, además de typedefs y afirmaciones estáticas. Sus argumentos y valor de retorno deben tener tipos literales. Se puede utilizar con argumentos de expresión no constante, pero cuando se hace esto, 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 ni 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 constantese comporta como si estuviera declarado constante, 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 utilizar 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 expresiones constantes

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 afirmaciones estáticas. 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 todos 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:

objetos constexprson constantes y se inicializan con valores conocidos durante la compilación; Las funciones constexpr producen resultados en tiempo de compilación cuando se llaman con argumentos cuyos valores se conocen durante la compilación; Los objetos y funciones constexpr se pueden utilizar en una gama más amplia de contextos que los objetos y funciones que no son constexpr; constexpr es parte de la interfaz de un objeto o función.

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

Respondido al

1 de enero de 2016 a las 7:45

zangw

zangw

45,3k

21

21 insignias de oro

185

185 insignia de platas

221

221 insignias de bronce

3

Gracias por el excelente código de ejemplo que muestra las diferentes situaciones. Por muy buenas que sean algunas de las otras explicaciones, ver el código en acción me pareció mucho más útil y comprensible. Realmente me ayudó a solidificar mi comprensión de lo que está sucediendo.

-RTHarston

9 febrero 2019 a las 8:56

Gracias, esto me ayudó a comprender mejor que la respuesta seleccionada.

-LoneCodeRanger

8 de noviembre de 2020 a las 11:49

Sin embargo,

numeric_limits<int>::max() es una expresión constante. (ya que es constexpr)

- manzana manzana

5 de noviembre de 2022 a las 22:04



------------------------------------

Tanto const como constexpr se pueden aplicar a variables y funciones. Aunque son similares entre sí, en realidad sonconceptos muy diferentes.

Tanto const como constexpr significan que sus valores no se pueden cambiar después de su inicialización. Entonces, 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. Si bien los valores de las variables const 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 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.

Así que las variables constantes pueden definir tanto el tiempo de compilación como el tiempo de compilación.Constantes como tamaño1 que se pueden usar para especificar tamaños de matriz y constantes de tiempo de ejecución como tamaño2 que se conocen solo en tiempo de ejecución y no se pueden usar para definir tamaños de matriz. Por otro lado, constexpr siempre define constantes de tiempo de compilación que pueden especificar tamaños de matriz.

Tanto const como constexpr también se pueden aplicar a funciones. Una función const debe ser una función miembro (método, operador) donde la aplicación de la palabra clave const significa que el método no puede cambiar los valores de sus campos miembro (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.
   }
};

Una 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 se pasan constantes de tiempo de compilación 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, la constexprLas funciones son las funciones normales de C++ que se pueden llamar incluso si se pasan argumentos no constantes. Pero en ese caso obtendrás 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.

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

1

Además, en C, constexpr int existe pero se escribe const int

- chico curioso

18 de enero de 2019 a las 22:22



------------------------------------

Según el libro "The C++ Programming Language 4th Editon" de Bjarne Stroustrup • const: que significa aproximadamente “Prometo no cambiar este valor” (§7.5). Esto se utiliza principalmente para especificar interfaces, de modo que los datos puedan pasarse a funciones sin temor a que se modifiquen. El compilador hace cumplir la promesa hecha por const. • constexpr: que significa aproximadamente “a evaluar en tiempo de compilación” (§10.4). Esto se utiliza 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.4∗square(var); // error : var is not a constant expression
const double max3 = 1.4∗square(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 estar definido constexpr. Por ejemplo:

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

Para ser constexpr, unLa función debe ser bastante simple: simplemente una declaración de retorno que calcula un valor. A La función constexpr se puede utilizar para argumentos no constantes, pero cuando se hace, el resultado no es un 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, por lo 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 casos (§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 problemas de desempeño, la noción de inmutabilidady (de un objeto con un estado inmutable) es un importante preocupación de diseño (§10.4).

1

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 que no es constexpr. Además, si tenemos un valor constante, ¿deberíamos preferir "const" o "constexpr"? (Más bien, un ensamblaje generado por una pregunta de estilo tiene el mismo aspecto)

-CoffeDeveloper

11 de enero de 2014 a las 10:58



------------------------------------

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

Una 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 anterior se compila bien y he comentado aquellos que provocan el error.

Las nociones clave a tener en cuenta aquí son las nociones de tiempo de compilación y tiempo de ejecución. Se han introducido nuevas innovaciones en C++ con la intención de **saber** lo más posible ciertas cosas en el momento de la compilación para mejorar el rendimiento en tiempo de ejecución.

Cualquier intento de explicación que noLo que no implica las dos nociones clave anteriores, es una alucinación.

1

esta es la mejor respuesta. las otras respuestas divagan durante demasiado tiempo

- uzumaki

2 de julio de 2023 a las 11:18



------------------------------------

Como ya señaló @0x499602d2, const solo garantiza que un valor no se pueda cambiar después de la inicialización, mientras que constexpr (introducido en C++11) garantiza la variación.able 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



------------------------------------

No creo que ninguna de las respuestas deje claro exactamente qué efectos secundarios tiene, ni qué 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 inicializado por cualquier función, pero constexpr inicializado por un no constexpr (una función que no está marcada con constexpr o una expresión no constexpr) generará un error de compilación. Tanto constexpr como const son implícitamente internos.enlace general para 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án un símbolo interno a menos que se exprese con extern, es decir, extern constexpr/const int i = 3; debe usarse). En una función, constexpr hace que la función nunca llegue permanentemente a la etapa de vinculación (independientemente de si es externa o en línea en la definición o -O0 o -Ofast), mientras que const nunca lo hace, y static e inline solo tienen este efecto en -O1 y superiores. Cuando una variable const/constexpr se inicializa mediante una función constexpr, la carga siempre se optimizacon cualquier indicador de optimización, pero nunca se optimiza si la función es solo estática o en línea, o si la variable no es 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;
} 

se compila en

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;
}

Se compila en

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 variable de alcance de archivo const/constexpr ocurra en tiempo de compilación y no produzca ningún símbolo global, mientras que no usarla causa que la inicialización ocurra antes de main en tiempo de ejecución.

Compilando usando -Ofast

¡Incluso -Ofast no optimiza la carga! https://godbolt.org/z/r-mhif, entonces necesitas constexpr

Las funciones constexpr también se pueden llamar desde dentro de otras funciones constexpr para obtener el mismo resultado. constexpr en una función también evita el uso decualquier 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 alcance de archivo (lo cual las funciones no pueden hacer en C, pero sí pueden hacerlo en C++ porque permite la inicialización dinámica). de variables de alcance de archivo), excepto que la función no puede exportar un símbolo global/local al vinculador también, incluso usando extern/static, que podría hacerlo en línea en C; Las funciones de asignación de variables de alcance de bloque se pueden incorporar simplemente usando una optimización -O1 sinconstexpr en C y C++.

2

Buen punto sobre el vinculador. ¿Podría considerarse más seguro en general usar constexpr ya que genera menos fugas de símbolos?

-Goblinhack

21 de mayo de 2020 a las 17:33

1

@NeilMcGill en realidad no, porque lo integrado y lo estático harán que el compilador no funcione.No emitir un símbolo local para multiplicar si se 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ático o en línea antes de la función. También olvidé algo más. Constexpr es la única palabra clave que no emite un símbolo para la función en -O0, estática y en línea.

- Lewis Kelsey

22 de mayo de 2020 a las 19:00



------------------------------------

Una descripción general de las palabras clave const y constexpr

En C++, si un objeto constante se inicializa con una expresión constante, cUtilice nuestro objeto constante siempre que se requiera una expresión constante.

const int x = 10;
int a[x] = {0};

Por ejemplo, podemos hacer una declaración de caso en switch.

constexpr se puede utilizar con matrices.

constexpr no es un tipo.

La palabra clave constexpr se puede utilizar junto con la palabra clave auto.

constexpr auto x = 10;

struct Data {   // We can make a bit field element of struct.   
    int a:x;
 };

Si inicializamos un objeto constante con una expresión constante, la expresión generada por ese objeto constante ahora también es una expresión constante.

Expresión constante: una expresión cuyo valor se puede calcular en el momento de la compilación.

x*5-4 // Esta es una expresión constante. Para el compilador, no hay diferencia entre escribir esta expresión y escribir 46 directamente.

La inicialización es obligatoria. Se puede utilizar únicamente con fines de lectura. No se puede cambiar. Hasta este punto, hayNo hay diferencia entre el método "const" y "constexpr" palabras clave.

NOTA: Podemos usar constexpr y const en la misma declaración.

constexpr const int* p;
Funciones Constexpr

Normalmente, el valor de retorno de una función se obtiene en tiempo de ejecución. Pero las llamadas a funciones constexpr se obtendrán como una constante en tiempo de compilación cuando se cumplan ciertas condiciones.

NOTA: Argumentos enviados a la variable de parámetro de la función en llamadas a función o a todas las variables de parámetro si hay más de un parámetro, si C.E el valor de retorno de la función se calculará en tiempo de compilación. !!!

constexpr int square (int a){
return a*a;
}

constexpr int a = 3;
constexpr int b = 5;

int arr[square(a*b+20)] = {0}; //This expression is equal to int arr[35] = {0};

Para que una función sea una función constexpr, el tipo de valor de retorno de la función y el tipo de los parámetros de la función deben estar en la categoría de tipo llamada "literal".escriba".

Las funciones constexpr son funciones implícitamente en línea.

Un punto importante:

Ninguna de las funciones constexpr necesita ser llamada con una expresión constante. No es obligatorio. Si esto sucede, el cálculo no se realizará en tiempo de compilación. Será tratado como una llamada de función normal. Por lo tanto, cuando se requiera la expresión constante, ya no podremos usar esta expresión.

Las condiciones requeridas para ser una función constexpr se muestran a continuación;

1 ) Los tipos utilizados en los parámetros de la función y el tipo del valor de retorno de la función deben ser de tipo literal.

2 ) No se debe utilizar una variable local con tiempo de vida estático dentro de la función.

3 ) Si la función es legal, cuando llamamos a esta función con una expresión constanteen tiempo de compilación, el compilador calcula el valor de retorno de la función en tiempo de compilación.

4) El compilador necesita ver el código de la función, por lo que las funciones constexpr casi siempre estarán en los archivos de encabezado.

5) Para que la función que creamos sea una función constexpr, la definición de la función debe estar en el archivo de encabezado. Por lo tanto, cualquier archivo fuente que incluya ese archivo de encabezado verá la definición de la función.

Prima

Normalmente, con la inicialización de miembros predeterminada, los miembros de datos estáticos con tipos constantes e integrales se pueden inicializar dentro de la clase. Sin embargo, para hacer esto, debe haber valores "constantes" y "tipos integrales".

Si usamos constexpr estático entonces no tiene que ser un tipo integral para inicializarlo dentro de la clase.Mientras lo inicialice con una expresión constante, no hay problema.

class Myclass  {
         const static int sx = 15;         // OK
         constexpr static int sy = 15;     // OK
         const static double sd = 1.5;     // ERROR
         constexpr static double sd = 1.5; // OK
 };

2

2

"si un objeto constante se inicializa con una expresión constante, podemos usar nuestro objeto constante siempre que se requiera una expresión constante." Sólo es cierto para constantes de tipos integrales.

- Santo Gato Negro

22/02/2021 a las 13:30

¿Es correcto el comentario? Obtengo cuadrado (a*b+20) -> cuadrado(35) -> 1225...

- Jay Elston

11 de mayo de 2022 a las 17:11



------------------------------------

En primer lugar, ambos son calificadores en C++. Una variable declarada const debe inicializarse y no se puede cambiar en el futuro. Por lo tanto, generalmente una variable declarada como constante tendrá un valor incluso antes de compilarse.

Pero para constexpr es un poco diferente.

Para constexpr, puede dar una expresión que pueda evaluarse durante la compilación del programa.

Obviamente la variable declarada como constexper no se puede cambiard en el futuro al igual que const.

Su guía para un futuro mejor - libreflare
Su guía para un futuro mejor - libreflare