Por que não podemos criar objetos trivialmente construtíveis usando malloc se o construtor trivial padrão não executa nenhuma ação?

14

Tenho dificuldade em entender o parágrafo a seguir citado na cppreference sobre o construtor padrão trivial. Pesquisei stackoverflow, mas ainda não recebi uma resposta clara. Então por favor ajude.

Um construtor padrão trivial é um construtor que não executa nenhuma ação. Todos os tipos de dados compatíveis com a linguagem C (tipos POD) são trivialmente construtíveis por padrão. Diferente de C, no entanto, objetos com construtores padrão triviais não podem ser criados simplesmente reinterpretando o armazenamento alinhado adequadamente, como a memória alocada com std :: malloc: placement-new é necessária para introduzir formalmente um novo objeto e evitar possíveis comportamentos indefinidos.

Especificamente, se o construtor padrão trivial não faz nada, por que não podemos reinterpretar o armazenamento e fingir que há um objeto com o tipo especificado? Você poderia fornecer alguns exemplos para o possível comportamento indefinido que isso causaria?

Liu Sha
fonte
O trabalho mais importante de um compilador não é compilar o código-fonte, mas rejeitar o código possivelmente inválido. Não é possível fazer isso quando você usa malloc ().
Hans Passant
6
A razão é muito simples. Quanto menos oportunidades houver para o programador fazer coisas malucas, mais oportunidades haverá para o compilador fazer coisas malucas (otimizações agressivas).
n. 'pronomes' m.
11
Por razões semelhantes, você não pode simplesmente *reinterpret_cast<float*>(&someNonFloatObject) = 0.1f;. O C ++ tem um conceito de objetos e vida útil do objeto, especificado na máquina abstrata, e apenas porque não há instruções da CPU para criar um objeto a partir do armazenamento não significa que não há diferença na máquina abstrata.
precisa
11
@HansPassant Um compilador que rejeita todo o código rejeita todo o código inválido. De qualquer forma, não é tarefa do copilador rejeitar programas que possuem UB.
n. 'pronomes' m.
11
stackoverflow.com/q/52154744
StoryTeller - Unslander Monica

Respostas:

7

P0593R5 fornece este exemplo:

struct X { int a, b; };
X *make_x() {
  X *p = (X*)malloc(sizeof(struct X));
  p->a = 1;
  p->b = 2;
  return p;
}

e explica:

Quando compilado com um compilador C ++, esse código tem um comportamento indefinido, porque p-> a tenta gravar em um subobjeto int de um objeto X, e esse programa nunca criou um objeto X nem um subobjeto int.

Pelo [intro.object] p1,

Um objeto é criado por uma definição, por uma nova expressão, ao alterar implicitamente o membro ativo de uma união ou quando um objeto temporário é criado.

... e este programa não fez nada disso.

Na prática, isso funciona e a situação da UB é considerada mais um defeito no padrão do que qualquer outra coisa. Todo o objetivo do artigo é propor uma maneira de corrigir esse problema e casos semelhantes sem interromper outras coisas.

AProgrammer
fonte
1

Por motivo de "pureza".

A alternativa e o status quo real eram que todas as regiões de armazenamento conteriam todos os objetos que se encaixam nesse armazenamento, ao mesmo tempo. Alguns membros do comitê não se sentem à vontade com o status quo e muitas pessoas temiam a noção de ter infinitos objetos no mesmo local (em um estado virtual e não inicializado).

Ninguém jamais foi capaz de mostrar um problema lógico por ter infinitos objetos em uma região de armazenamento.

Por terem diferentes seções do padrão dizendo coisas contraditórias, os membros do comitê decidiram levar a sério uma das piores partes do padrão.

Além disso, o uso de literais de cadeia de caracteres não é estritamente permitido, se você realmente levar a sério essa parte do padrão.

curiousguy
fonte
o uso de literais de string não é estritamente permitido . Há um problema semelhante no CWG sobre type_infoobjetos. Você relatou 'sobre literais de string?
Language Lawyer