Considere los siguientes tres struct
s:
class blub {
int i;
char c;
blub(const blub&) {}
};
class blob {
char s;
blob(const blob&) {}
};
struct bla {
blub b0;
blob b1;
};
En plataformas típicas donde int
hay 4 bytes, los tamaños, las alineaciones y el relleno total 1 son los siguientes:
struct size alignment padding
-------- ------ ----------- ---------
blub 8 4 3
blob 1 1 0
bla 12 4 6
No hay superposición entre el almacenamiento de los miembros blub
y blob
, a pesar de que el tamaño 1 blob
podría, en principio, "encajar" en el relleno de blub
.
C ++ 20 introduce el no_unique_address
atributo, que permite que los miembros vacíos adyacentes compartan la misma dirección. También permite explícitamente el escenario descrito anteriormente de usar el relleno de un miembro para almacenar otro. De cppreference (énfasis mío):
Indica que este miembro de datos no necesita tener una dirección distinta de todos los demás miembros de datos no estáticos de su clase. Esto significa que si el miembro tiene un tipo vacío (por ejemplo, Asignador sin estado), el compilador puede optimizarlo para que no ocupe espacio, como si fuera una base vacía. Si el miembro no está vacío, cualquier relleno de cola también se puede reutilizar para almacenar otros miembros de datos.
De hecho, si usamos este atributo en blub b0
, el tamaño de las bla
gotas disminuye 8
, por lo que blob
se almacena en el blub
tal como se ve en godbolt .
Finalmente, llegamos a mi pregunta:
¿Qué texto en los estándares (C ++ 11 a C ++ 20) evita esta superposición sin no_unique_address
, para los objetos que no se pueden copiar trivialmente?
Necesito excluir objetos trivialmente copiables (TC) de lo anterior, porque para los objetos TC, se permite std::memcpy
de un objeto a otro, incluidos los subobjetos de miembros, y si el almacenamiento se superpone, esto se rompería (porque todo o parte del almacenamiento para el miembro adyacente se sobrescribirá) 2 .
1 Calculamos el relleno simplemente como la diferencia entre el tamaño de la estructura y el tamaño de todos sus miembros constituyentes, de forma recursiva.
2 Es por eso que tengo definidos los constructores de copia: para hacer blub
y blob
no trivialmente copiable .