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:

  1. Minha conclusão é verdadeira?
  2. 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?
  3. C ++ 0x (C ++ 11, se preferir) muda alguma coisa?

Obrigado por quaisquer insights!

Foi útil?

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:

  1. Em C ++ 03, apenas expressões integrais podem ser expressões constantes.
  2. Porque o padrão diz isso (naturalmente).
  3. 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-chave constexpr em vez de const 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.
}
Licenciado em: CC-BY-SA com atribuição
Não afiliado a StackOverflow
scroll top