Розглянемо наступні три structs:
class blub {
int i;
char c;
blub(const blub&) {}
};
class blob {
char s;
blob(const blob&) {}
};
struct bla {
blub b0;
blob b1;
};
На типових платформах, де intрозміщено 4 байти, розміри, вирівнювання та загальна заливка 1 є такими:
struct size alignment padding
-------- ------ ----------- ---------
blub 8 4 3
blob 1 1 0
bla 12 4 6
Між сховищами blubта blobелементами немає перекриття , хоча розмір 1 blobв принципі міг би "поміститися" в набивання blub.
C ++ 20 вводить no_unique_addressатрибут, який дозволяє сусіднім порожнім членам ділити ту саму адресу. Це також явно дозволяє описаний вище сценарій використання прокладки одного члена для зберігання іншого. З cppreference (моє наголос):
Вказує, що цей член даних не повинен мати адресу, відмінну від усіх інших нестатичних членів даних свого класу. Це означає, що якщо член має порожній тип (наприклад, Allocator без стану), компілятор може оптимізувати його, щоб він не займав місця, як, якби це була порожня база. Якщо член не порожній, будь-які хвостові накладки в ньому також можуть бути повторно використані для зберігання інших членів даних.
Дійсно, якщо ми будемо використовувати цей атрибут на blub b0, розмір blaкрапель до 8, тож blobсправді зберігається в тому blub , що видно на Godbolt .
Нарешті, ми переходимо до мого питання:
Який текст у стандартах (C ++ 11 до C ++ 20) перешкоджає цьому накладенню без no_unique_addressоб’єктів, які не можна тривіально скопіювати?
Мені потрібно виключити тривіально копіювані (ТС) об’єкти із вищезазначеного, тому що для об'єктів ТС дозволено переходити std::memcpyвід одного об’єкта до іншого, включаючи суб’єкти членів, і якщо сховище буде перекрито, це порушиться (тому що все або частина сховища для сусіднього члена буде переписано) 2 .
1 Ми обчислюємо прокладку просто як різницю між розмірами конструкції та розміром усіх її складових, рекурсивно.
2 Тому у мене визначені конструктори копій: робити, blubа blobне тривіально копіювати .