Uso da variável no próprio inicializador

22

[basic.scope.pdecl] / 1 do rascunho padrão do C ++ 20 teve o seguinte exemplo (não normativo) em uma nota (cotação parcial de antes da mesclagem da solicitação pull 3580 , consulte a resposta a esta pergunta):

unsigned char x = x;

[...] x é inicializado com seu próprio valor (indeterminado).

Isso realmente tem um comportamento bem definido no C ++ 20?


Geralmente, a auto-inicialização do formulário T x = x;tem um comportamento indefinido em virtude do xvalor de ser indeterminado antes da conclusão da inicialização. A avaliação de valores indeterminados geralmente causa um comportamento indefinido ( [basic.indent] / 2 ), mas há uma exceção específica em [basic.indent] /2.3 que permite inicializar diretamente uma unsigned charvariável de um lvalue unsigned charcom valor indeterminado (causando inicialização com um valor indeterminado) )

Isso, por si só, não causa um comportamento indefinido, mas causaria outros tipos Tque não são tipos de caracteres estreitos não assinados ou std::byte, por exemplo int x = x;. Essas considerações aplicadas no C ++ 17 e antes também, consulte também as perguntas vinculadas na parte inferior.

No entanto, mesmo para unsigned char x = x;, o [basic.lifetime] / 7 do rascunho atual diz:

Da mesma forma, antes que a vida útil de um objeto comece [...] a usar as propriedades do glvalue que não dependem de seu valor, está bem definido. O programa tem comportamento indefinido se:

  • o glvalue é usado para acessar o objeto, ou

  • [...]

Isso parece sugerir que xo valor do exemplo só pode ser usado durante sua vida útil.

[basic.lifetime] / 1 diz:

[...]

A vida útil de um objeto do tipo T começa quando:

  • [...] e
  • sua inicialização (se houver) está completa (incluindo inicialização vazia) ([dcl.init]),

[...]

Assim x, a vida útil começa apenas após a inicialização ser concluída. Mas, no exemplo citado x, o valor é usado antes xda inicialização da conclusão. Portanto, o uso tem um comportamento indefinido.

Minha análise está correta e, se estiver, afeta casos semelhantes de uso antes da inicialização, como

int x = (x = 1);

quais, até onde eu sei, estavam bem definidas no C ++ 17 e também antes?


Observe que no C ++ 17 (rascunho final) o segundo requisito para o início da vida útil era diferente :

  • se o objeto tiver inicialização não-vazia, sua inicialização estará completa,

Como a xinicialização do C ++ 17 teria uma inicialização vaga (mas não a que está no rascunho atual), sua vida útil já teria começado quando for acessada no inicializador nos exemplos fornecidos acima e, nos dois exemplos, não houve comportamento indefinido devido à vida útil xem C ++ 17.

A redação anterior ao C ++ 17 é novamente diferente, mas com o mesmo resultado.


A questão não é sobre comportamento indefinido ao usar valores indeterminados, que foram abordados, por exemplo, nas seguintes perguntas:

noz
fonte
@LanguageLawyer Não tenho certeza de que estou correto, principalmente se ninguém responder ainda. Se outras pessoas concordarem comigo aqui, eu posso arquivar um mais tarde (ou talvez alguém o faça antes de mim), mas não quero arquivar problemas dos quais não tenho certeza.
noz
@LanguageLawyer: Não pode ser uma questão editorial se o documento de trabalho disser inequivocamente a coisa errada.
Davis Herring
11
A palavra é alterada por P1358 .
X64xzr # 26/19
11
@xskxzr Certo, e nesse meio tempo o LanguageLawyer também registrou uma edição editorial , que parece ter sido encaminhada ao CWG para esclarecimento de intenção.
walnut
11
@ clockw0rk int x ^= x;não está sintaticamente bem formado. Você pode ter uma definição de variável com inicializador (ou seja int x = x;, UB) ou uma instrução de expressão de atribuição xor (ou seja x ^= x;, embora seja UB se xfor do tipo int, foi inicializada por padrão e não foi atribuída anteriormente). Você não pode misturar esses dois em um.
walnut

Respostas:

8

Isso foi aberto como uma edição editorial . Foi encaminhado ao CWG para discussão (interna). Aproximadamente 24 horas depois, a pessoa que encaminhou o problema criou uma solicitação pull que modifica o exemplo para deixar claro que esse é o UB:

Aqui, a inicialização do segundo \ tcode {x} tem um comportamento indefinido, porque o inicializador acessa o segundo \ tcode {x} fora de sua vida útil \ iref {basic.life}.

Esse PR foi adicionado e o problema foi encerrado. Portanto, parece claro que a interpretação óbvia (UB devido ao acesso a um objeto cuja vida útil não foi iniciada) é a interpretação pretendida. Parece que a intenção do comitê é tornar essas construções não funcionais, e o texto não normativo do padrão foi atualizado para refletir isso.

Nicol Bolas
fonte