¿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?
¿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?
Respuestas:
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 PI1
y PI2
son 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. PI1
puede 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.
const int N = 10; char a[N];
funciona, y los límites de la matriz deben ser constantes de tiempo de compilación.
PI1
a 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 PI1
un tipo integral me parece un poco impredecible.
enum
inicializador son las dos únicas diferencias notables entre const
y constexpr
(y ninguno funciona de double
todos modos).
1 / PI1
y 1 / PI2
puede dar resultados diferentes. Sin embargo, no creo que este tecnicismo sea tan importante como el consejo de esta respuesta.
constexpr double PI3 = PI1;
funciona correctamente para mí. (MSVS2013 CTP). ¿Qué estoy haciendo mal?
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);
s0
es una constante, pero no promete ser inicializada en tiempo de compilación. s1
está marcado constexpr
, por lo que es una constante y, dado que S
el 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
constexpr
conducirí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 const
y no como constexpr
: es decir, ¿ constexpr int foo(S)
se ejecutaría en tiempo de compilación si llamo foo(s0)
?
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);
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.
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:
Referencia: "Programación: Principios y práctica usando C ++" por Stroustrup