Existe uma diferença entre as seguintes definições?
const double PI = 3.141592653589793;
constexpr double PI = 3.141592653589793;
Caso contrário, qual estilo é preferido no C ++ 11?
Existe uma diferença entre as seguintes definições?
const double PI = 3.141592653589793;
constexpr double PI = 3.141592653589793;
Caso contrário, qual estilo é preferido no C ++ 11?
Respostas:
Eu acredito que há uma diferença. Vamos renomeá-los para que possamos falar sobre eles mais facilmente:
const double PI1 = 3.141592653589793;
constexpr double PI2 = 3.141592653589793;
Ambos PI1
e PI2
são constantes, o que significa que você não pode modificá-los. No entanto, apenas PI2
é uma constante em tempo de compilação. Ele deve ser inicializado em tempo de compilação. PI1
pode ser inicializado em tempo de compilação ou tempo de execução. Além disso, somente PI2
pode ser usado em um contexto que requer uma constante em tempo de compilação. Por exemplo:
constexpr double PI3 = PI1; // error
mas:
constexpr double PI3 = PI2; // ok
e:
static_assert(PI1 == 3.141592653589793, ""); // error
mas:
static_assert(PI2 == 3.141592653589793, ""); // ok
Quanto ao que você deve usar? Use o que atenda às suas necessidades. Deseja garantir que você tenha uma constante de tempo de compilação que possa ser usada em contextos em que é necessária uma constante em tempo de compilação? Deseja inicializá-lo com um cálculo feito em tempo de execução? Etc.
const int N = 10; char a[N];
works e limites da matriz devem ser constantes em tempo de compilação.
PI1
em uma constante integral em tempo de compilação para uso em uma matriz, mas não como um parâmetro de modelo integral que não seja do tipo. Portanto, a conversibilidade em tempo de compilação PI1
para um tipo integral me parece um pouco difícil.
enum
inicializador são as únicas duas diferenças notáveis entre const
e constexpr
(e nem funcionam de double
qualquer maneira).
1 / PI1
e 1 / PI2
pode produzir resultados diferentes. Eu não acho que esse tecnicismo seja tão importante quanto os conselhos nesta resposta, no entanto.
constexpr double PI3 = PI1;
funciona corretamente para mim. (MSVS2013 CTP). O que estou fazendo de errado?
Não há diferença aqui, mas importa quando você tem um tipo que tem um construtor.
struct S {
constexpr S(int);
};
const S s0(0);
constexpr S s1(1);
s0
é uma constante, mas não promete ser inicializado em tempo de compilação. s1
é marcado constexpr
, portanto é uma constante e, como S
o construtor também é marcado constexpr
, ele será inicializado em tempo de compilação.
Principalmente isso importa quando a inicialização em tempo de execução consumiria muito tempo e você deseja enviar esse trabalho para o compilador, onde também consome tempo, mas não diminui o tempo de execução do programa compilado.
constexpr
levaria a um diagnóstico caso a computação do objeto em tempo de compilação fosse impossível. O que é menos claro é se uma função que espera um parâmetro constante possa ser executada em tempo de compilação, caso o parâmetro seja declarado como const
e não como constexpr
: ou seja, seria constexpr int foo(S)
executado em tempo de compilação se eu chamar foo(s0)
?
foo(s0)
que seja executado em tempo de compilação, mas você nunca sabe: é permitido a um compilador fazer essas otimizações. Certamente, nem gcc 4.7.2 nem clang 3.2 permitir-me para compilarconstexpr a = foo(s0);
constexpr indica um valor constante e conhecido durante a compilação.
const indica um valor que é apenas constante; não é obrigatório saber durante a compilação.
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
Observe que const não oferece a mesma garantia que constexpr, porque os objetos const não precisam ser inicializados com valores conhecidos durante a compilação.
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 os objetos constexpr são const, mas nem todos os objetos const são constexpr.
Se você deseja que os compiladores garantam que uma variável tenha um valor que possa ser usado em contextos que exigem constantes em tempo de compilação, a ferramenta a ser alcançada é constexpr, não const.
Uma constante simbólica constexpr deve receber um valor conhecido em tempo de compilação. Por exemplo:
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 lidar com casos em que o valor de uma “variável” inicializado com um valor que não é conhecido no momento da compilação, mas nunca muda após a inicialização, o C ++ oferece uma segunda forma de constante (uma const ). Por exemplo:
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
}
Tais " variáveis const " são muito comuns por dois motivos:
Referência: "Programação: princípios e práticas usando C ++" por Stroustrup