Observe que esta é uma pergunta de direito linguístico . Conheço o significado comum de "variável", mas estou tentando entender exatamente o que o padrão chama de "variáveis". Estou escrevendo um material didático e quero usar as definições corretas.
Variáveis são comumente definidas como "objetos nomeados", ou mais corretamente "objetos que foram declarados" (já que variáveis podem não ter nome, mas são sempre declaradas).
Mas há um segundo ponto de vista: variáveis são um conceito de tempo de compilação. Segundo essa definição, uma declaração no código-fonte sempre corresponde a uma variável, mesmo que, em tempo de execução, vários objetos sejam criados a partir dela.
Qual está correta?
Aqui está um exemplo em que isso é importante. Digamos que você tenha
void foo(bool a)
{
int x = 42;
if (a)
foo(false);
}
Quando você chama foo(true)
, duas instâncias de int x = 42;
get existem ao mesmo tempo.
Eles são claramente dois objetos diferentes, mas tecnicamente são variáveis diferentes ou a mesma variável?
Se variáveis são uma coisa de tempo de compilação, então as duas são a mesma variável.
A segunda definição parece errada à primeira vista, mas aqui temos um membro do comitê C++ argumentando que ela é a correta , por exemplo.
Com a ressalva de que minha opinião não é totalmente independente estatisticamente daquela citada, concordo plenamente: o uso de "variável" no padrão faz muito mais sentido interpretado como "o sujeito abstrato de declarações de certas formas" do que como "um espaço de memória considerado como tendo um certo tipo". O padrão frequentemente precisa discutir o nome de uma variável, o escopo dentro do qual ela é declarada, se duas declarações (talvez em unidades de tradução diferentes) declaram a mesma variável, se uma variável é uma referência ou não, e assim por diante, e essas coisas claramente pertencem à construção singular de tempo de tradução, e não a qualquer coisa que exista durante a execução. A evidência mais forte para este último ponto é que todas as regras sobre, digamos, uma variável ser utilizável em expressões constantes se aplicam mesmo se for uma variável local não estática em uma função que nunca é chamada.
Por outro lado, a palavra "objeto" é usada principalmente de maneiras que correspondem ao conceito de "dados reais": ela tem um valor e um endereço e pode estar dentro ou fora do tempo de vida. Isso faz sentido porque o padrão deve suportar todas as operações de tempo de execução em objetos criados por
new
(posicionamento ou não), que obviamente não são variáveis.Infelizmente, o padrão não é totalmente consistente aqui, dependendo excessivamente da compreensão intuitiva do leitor sobre a intenção ao dizer coisas como "Uma variável é introduzida pela declaração [...] de um objeto" ([basic.pre]/7) e "variáveis de bloco com duração de armazenamento automática [...] são inicializadas na ordem de declaração" ([stmt.dcl]/2). Algum progresso foi feito (o C++17 parou de chamar
*this
uma "entidade" como se ela pudesse ser encontrada por meio de consulta de nome ou algo assim), e mais está por vir (o artigo do Reflection está fazendo uma limpeza adicional necessária para descrever o que^^x
significa).O padrão C++ especifica (de forma incompleta) o comportamento de uma máquina abstrata que executa o código que você escreve.
Mapear isso para o hardware real fica por conta do compilador; desde que o programa resultante se comporte "como se" fosse a máquina abstrata, o programa estará em conformidade.
Isso permite que os compiladores façam coisas como eliminar objetos completamente, desde que o programa se comporte "como se" o objeto existisse. Os bits reais no hardware em que o programa está sendo executado não são limitados, exceto que, em alguns casos, pode ser incrivelmente impraticável não se alinharem com os bits esperados.
Esta opção "como se" é geralmente usada por compiladores em situações em que o compilador tem conhecimento quase total dos possíveis usos do objeto. Ou seja, variáveis locais para as quais ninguém examina ponteiros. O padrão afirma que algumas operações são comportamentos indefinidos (percorrer a pilha manualmente, por exemplo, requer comportamento indefinido em C++); isso significa que ele pode provar que ninguém acessou "legalmente" uma variável e, como tal, a existência dessa variável pode ser eliminada (ou alterada para outra).
No seu programa de exemplo, ninguém acessa
x
, então ele não precisa existir em tempo de execução. E, de fato, se você examinar o código produzido por um compilador otimizador, ele estará ausente.