const vs constexpr en variables

303

¿Hay alguna diferencia entre las siguientes definiciones?

const     double PI = 3.141592653589793;
constexpr double PI = 3.141592653589793;

Si no, ¿qué estilo se prefiere en C ++ 11?

flujo libre
fuente
2
Superset: stackoverflow.com/questions/14116003/…
Ciro Santilli 郝海东 冠状 病 六四 事件 法轮功
Ambos son constantes en tiempo de compilación. Pero puedes hacer un const_cast del primero y escribirle. Pero cualquier compilador lo optimizará, ya que esto no influye en las "lecturas" como suceden en tiempo de compilación.
Bonita Montero

Respuestas:

347

Creo que hay una diferencia. Cambiemos el nombre para que podamos hablar sobre ellos más fácilmente:

const     double PI1 = 3.141592653589793;
constexpr double PI2 = 3.141592653589793;

Ambos PI1y PI2son constantes, lo que significa que no puede modificarlos. Sin embargo, solo PI2 es una constante de tiempo de compilación. Se deberá ser inicializado en tiempo de compilación. PI1puede inicializarse en tiempo de compilación o en tiempo de ejecución. Además, solo PI2 se puede usar en un contexto que requiere una constante de tiempo de compilación. Por ejemplo:

constexpr double PI3 = PI1;  // error

pero:

constexpr double PI3 = PI2;  // ok

y:

static_assert(PI1 == 3.141592653589793, "");  // error

pero:

static_assert(PI2 == 3.141592653589793, "");  // ok

¿Cuál debería usar? Use el que satisfaga sus necesidades. ¿Desea asegurarse de tener una constante de tiempo de compilación que pueda usarse en contextos donde se requiere una constante de tiempo de compilación? ¿Desea poder inicializarlo con un cálculo realizado en tiempo de ejecución? Etc.

Howard Hinnant
fuente
6060
¿Estás seguro? Porque const int N = 10; char a[N];funciona, y los límites de la matriz deben ser constantes de tiempo de compilación.
fredoverflow
10
Estoy seguro de que los ejemplos que escribí van (probé cada uno de ellos antes de publicarlos). Sin embargo, mi compilador me permite convertir PI1a una constante integral en tiempo de compilación para usar en una matriz, pero no para usar como un parámetro de plantilla integral sin tipo. Entonces, la convertibilidad en tiempo de compilación de PI1un tipo integral me parece un poco impredecible.
Howard Hinnant
34
@FredOverflow: los índices de matriz no constantes han "funcionado" durante aproximadamente una década (por ejemplo, hay una extensión de g ++ para eso), pero eso no significa que sea estrictamente legal C ++ (aunque algunos estándares más recientes de C o C ++ lo hicieron legal , I olvidé cuál) En cuanto a las diferencias en las constantes de tiempo de compilación, los parámetros de plantilla y el uso como enuminicializador son las dos únicas diferencias notables entre consty constexpr(y ninguno funciona de doubletodos modos).
Damon
17
El párrafo 4 de 5.19 Expresiones constantes [expr.const] también es una nota (no normativa) que destaca que una implementación puede hacer aritmética de punto flotante de manera diferente (por ejemplo, con respecto a la precisión) en tiempo de compilación que en tiempo de ejecución. Entonces, 1 / PI1y 1 / PI2puede dar resultados diferentes. Sin embargo, no creo que este tecnicismo sea tan importante como el consejo de esta respuesta.
Luc Danton el
44
Pero constexpr double PI3 = PI1;funciona correctamente para mí. (MSVS2013 CTP). ¿Qué estoy haciendo mal?
NuPagadi
77

No hay diferencia aquí, pero es importante cuando tienes un tipo que tiene un constructor.

struct S {
    constexpr S(int);
};

const S s0(0);
constexpr S s1(1);

s0es una constante, pero no promete ser inicializada en tiempo de compilación. s1está marcado constexpr, por lo que es una constante y, dado que Sel constructor también está marcado constexpr, se inicializará en tiempo de compilación.

Principalmente, esto es importante cuando la inicialización en tiempo de ejecución lleva mucho tiempo y desea llevar ese trabajo al compilador, donde también lleva mucho tiempo, pero no ralentiza el tiempo de ejecución del programa compilado

Pete Becker
fuente
3
Estoy de acuerdo: la conclusión a la que llegué fue que constexprconduciría a un diagnóstico si el cálculo en tiempo de compilación del objeto fuera imposible. Lo que está menos claro es si una función que espera un parámetro constante podría ejecutarse en tiempo de compilación si el parámetro se declara como consty no como constexpr: es decir, ¿ constexpr int foo(S)se ejecutaría en tiempo de compilación si llamo foo(s0)?
Matthieu M.
44
@MatthieuM: Dudo si foo(s0)se ejecutaría en tiempo de compilación, pero nunca se sabe: un compilador puede hacer tales optimizaciones. Ciertamente, ni gcc 4.7.2 ni clang 3.2 me permiten compilarconstexpr a = foo(s0);
rici
50

constexpr indica un valor constante y conocido durante la compilación.
const indica un valor que solo es constante; No es obligatorio saberlo durante la compilación.

int sz;
constexpr auto arraySize1 = sz;    // error! sz's value unknown at compilation
std::array<int, sz> data1;         // error! same problem

constexpr auto arraySize2 = 10;    // fine, 10 is a compile-time constant
std::array<int, arraySize2> data2; // fine, arraySize2 is constexpr

Tenga en cuenta que const no ofrece la misma garantía que constexpr, porque los objetos const no necesitan inicializarse con valores conocidos durante la compilación.

int sz;
const auto arraySize = sz;       // fine, arraySize is const copy of sz
std::array<int, arraySize> data; // error! arraySize's value unknown at compilation

Todos los objetos constexpr son const, pero no todos los objetos const son constexpr.

Si desea que los compiladores garanticen que una variable tiene un valor que se puede usar en contextos que requieren constantes de tiempo de compilación, la herramienta para alcanzar es constexpr, no const.

Ajay yadav
fuente
2
Me gustó mucho tu explicación ... ¿puedes comentar más sobre dónde están los casos en que podríamos necesitar usar constantes de tiempo de compilación en escenarios de la vida real?
Mayukh Sarkar
1
@MayukhSarkar Simplemente Google C ++ por qué constexpr , por ejemplo stackoverflow.com/questions/4748083/…
underscore_d
18

A una constante simbólica constexpr se le debe dar un valor que se conoce en tiempo de compilación. Por ejemplo:

constexpr int max = 100; 
void use(int n)
{
    constexpr int c1 = max+7; // OK: c1 is 107
    constexpr int c2 = n+7;   // Error: we don’t know the value of c2
    // ...
}

Para manejar casos en los que el valor de una "variable" que se inicializa con un valor que no se conoce en el momento de la compilación pero que nunca cambia después de la inicialización, C ++ ofrece una segunda forma de constante (una constante ). Por ejemplo:

constexpr int max = 100; 
void use(int n)
{
    constexpr int c1 = max+7; // OK: c1 is 107
    const int c2 = n+7; // OK, but don’t try to change the value of c2
    // ...
    c2 = 7; // error: c2 is a const
}

Tales " variables constantes " son muy comunes por dos razones:

  1. C ++ 98 no tenía constexpr, por lo que las personas usaban const .
  2. Enumere el elemento "Variables" que no son expresiones constantes (su valor no se conoce en el momento de la compilación) pero que no cambian los valores después de la inicialización son en sí mismas ampliamente útiles.

Referencia: "Programación: Principios y práctica usando C ++" por Stroustrup

Jnana
fuente
25
Tal vez debería haber mencionado que el texto en su respuesta está tomado literalmente de "Programación: Principios y práctica usando C ++" por Stroustrup
Aky