Considere os três seguintes struct
:
class blub {
int i;
char c;
blub(const blub&) {}
};
class blob {
char s;
blob(const blob&) {}
};
struct bla {
blub b0;
blob b1;
};
Nas plataformas típicas, com int
4 bytes, os tamanhos, alinhamentos e preenchimento total 1 são os seguintes:
struct size alignment padding
-------- ------ ----------- ---------
blub 8 4 3
blob 1 1 0
bla 12 4 6
Não há sobreposição entre o armazenamento dos membros blub
e blob
, mesmo que o tamanho 1 blob
possa, em princípio, "encaixar" no preenchimento de blub
.
O C ++ 20 apresenta o no_unique_address
atributo, que permite que membros vazios adjacentes compartilhem o mesmo endereço. Também permite explicitamente o cenário descrito acima do uso de preenchimento de um membro para armazenar outro. De cppreference (ênfase minha):
Indica que esse membro de dados não precisa ter um endereço distinto de todos os outros membros de dados não estáticos de sua classe. Isso significa que se o membro tiver um tipo vazio (por exemplo, Alocador sem estado), o compilador pode otimizá-lo para não ocupar espaço, como se fosse uma base vazia. Se o membro não estiver vazio, qualquer preenchimento de cauda nele também poderá ser reutilizado para armazenar outros membros de dados.
De fato, se usarmos esse atributo em blub b0
, o tamanho de bla
cai para 8
, de modo que ele blob
é realmente armazenado no blub
como visto no godbolt .
Finalmente, chegamos à minha pergunta:
Que texto nos padrões (C ++ 11 a C ++ 20) impede essa sobreposição sem no_unique_address
, para objetos que não são trivialmente copiáveis?
Eu preciso excluir objetos trivialmente copiáveis (TC) do acima exposto, porque para objetos TC, é permitido passar std::memcpy
de um objeto para outro, incluindo subobjetos de membros, e se o armazenamento fosse sobreposto, isso seria interrompido (porque todo ou parte do armazenamento para o membro adjacente seria substituído) 2 .
1 Calculamos o preenchimento simplesmente como a diferença entre o tamanho da estrutura e o tamanho de todos os seus membros constituintes, recursivamente.
2 É por isso que tenho construtores de cópia definidos: tornar blub
e blob
não trivialmente copiáveis .