Por que um ponteiro constante não pode ser uma expressão constante?
-
29-10-2019 - |
Pergunta
O seguinte programa é compilado:
template <const int * P>
class Test{};
extern const int var = 42; //extern needed to force external linkage
int main()
{
Test<&var> test;
}
Este, no entanto, não, o que é uma surpresa para mim:
template <const int * P>
class Test{};
extern const int var = 42; //extern needed to force external linkage
extern const int * const ptr = &var; //extern needed to force external linkage
int main()
{
Test<ptr> test; //FAIL! Expected constant expression.
}
Exemplo alternativo:
int main()
{
const int size = 42;
int ok[*&size]; //OK
const int * const pSize = &size;
int fail[*pSize]; //FAIL
}
Concluí que um ponteiro não pode ser uma expressão constante, independentemente de ser const e inicializado com uma expressão constante.
Perguntas:
- Minha conclusão é verdadeira?
- Em caso afirmativo, por que um ponteiro não pode ser uma expressão constante?Se não, por que os programas acima não compilam?
- C ++ 0x (C ++ 11, se preferir) muda alguma coisa?
Obrigado por quaisquer insights!
Solução
É um pouco mais complicado. Em C ++ 03 e C ++ 11, &var
é uma expressão constante se var
for uma variável local estática / estática de classe ou de escopo de namespace. Isso é chamado de expressão de constante de endereço. A inicialização de uma variável de ponteiro de escopo estático de classe ou namespace com essa expressão constante é garantida antes de qualquer código ser executado (fase de inicialização estática), por ser uma expressão constante.
No entanto, apenas a partir do C ++ 11, uma variável de ponteiro constexpr que armazena o &var
do endereço também pode ser usada como uma expressão de constante de endereço e somente a partir do C ++ 11, você pode cancelar a referência de uma constante de endereço expressão (na verdade, você pode desreferenciar ainda mais - até mesmo endereços de elemento de matriz local, mas vamos mantê-lo ontopico) e se ele se refere a uma variável integral constante inicializada antes da desreferência ou uma variável constexpr, você novamente obtém uma expressão constante (dependendo de o tipo e a categoria de valor, o tipo de expressão constante pode variar). Como tal, o seguinte é C ++ 11 válido:
int const x = 42;
constexpr int const *px = &x;
// both the value of "px" and the value of "*px" are prvalue constant expressions
int array[*px];
int main() { return sizeof(array); }
Em caso afirmativo, por que um ponteiro não pode ser uma expressão constante? Se não, por que os programas acima não compilam?
Esta é uma limitação conhecida no texto do Padrão - atualmente, ele permite apenas outros parâmetros de modelo como argumentos ou & object
, para um parâmetro de modelo do tipo ponteiro. Mesmo que o compilador seja capaz de fazer muito mais.
Outras dicas
Ainda não é permitido em C ++ 0x. temp.arg.nontype
requer:
Um argumento-modelo para um parâmetro-modelo sem tipo e sem modelo deve ser um dos seguintes:
- para um parâmetro-modelo não-tipo do tipo integral ou enumeração, uma expressão constante convertida (5.19) do tipo do parâmetro-modelo; ou
- o nome de um parâmetro de modelo sem tipo; ou
- uma expressão constante (5.19) que designa o endereço de um objeto com duração de armazenamento estático e ligação externa ou interna ou uma função com ligação externa ou interna, incluindo modelos de função e ids de modelo de função, mas excluindo membros de classe não estáticos, expresso (ignorando parênteses) como
&
id-expression
, exceto que o & pode ser omitido se o nome se referir a uma função ou matriz e deverá ser omitido se o parâmetro-modelo correspondente for uma referência; ou- uma expressão constante que avalia um valor de ponteiro nulo (4.10); ou
- uma expressão constante que avalia como um valor de ponteiro de membro nulo (4.11); ou
- uma indicação para membro expresso conforme descrito em 5.3.1.
resposta original:
- Em C ++ 03, apenas expressões integrais podem ser expressões constantes.
- Porque o padrão diz isso (naturalmente).
- Em C ++ 0x, n3290 inclui exemplos usando
constexpr
em um ponteiro. Portanto, o que você está tentando agora deve ser possível, embora agora você deva usar a palavra-chaveconstexpr
em vez deconst
de nível superior.
Também há um bug gcc envolvido, g ++ rejeita os próprios exemplos do rascunho padrão de uso válido de constexpr
.
O problema é porque seu programa C ++ pode ser carregado em qualquer ponto da memória e, portanto, o endereço de um var
global pode ser diferente a cada vez que você executa o programa.O que acontece se você executar seu programa duas vezes?var
está obviamente em dois locais diferentes então.
Pior ainda, em seu exemplo, você pega o endereço de uma variável na pilha!olhe isso:
void myfunction( unsigned int depth) {
const int myvar = depth;
const int * const myptr = &myvar;
if (depth)
myfunction(depth-1);
}
Se main chamar myfunction (3), então 3 myvars serão criados em locais separados.Não há como o tempo de compilação nem mesmo saber quantos muitos myvars são criados, muito menos quais são os locais exatos.
Finalmente: declarar uma variável como const
significa: "Eu prometo" e não significa que é uma constante de tempo de compilação.Veja este exemplo:
int main(int argc, char** argv) {
const int cargc = argc;
char* myargs[cargc]; //the size is constant, but not a _compile time_ constant.
}