Давайте поиграемся в чуть более интересные игры.
Вспомним, что у нас в С++ есть правило: структура не может иметь нулевой размер. Потому что у ее инстанса должен быть адрес, и здесь С++ как-то забыл про Zero Cost и все такое.
Зато С++ вспомнил про Zero Cost, когда мы примешиваем пустую структуру в наследование другой.
struct Empty1 {};
struct Empty2 {};
struct NonEmpty {
int Value;
};
struct MyClass1
:public NonEmpty
,public Empty1
,public Empty2
{
};
struct MyClass2
:public Empty1
,public NonEmpty
,public Empty2
{
};
struct MyClass3
:public Empty1
,public Empty2
,public NonEmpty
{
};
static_assert(sizeof(NonEmpty) + sizeof(Empty1) + sizeof(Empty2) == 6);
static_assert(sizeof(int) == 4);
static_assert(sizeof(MyClass1) == 4);
static_assert(sizeof(MyClass2) == 4);
static_assert(sizeof(MyClass3) == 4);
int main() {}Вот эта штука скомпилируется. Тоесть у нас структуры без размера имеют размер, но этот размер не добавляется к размеру общей структуры.
Всвязи с чем я упоролся, и написал вот такой класс:
struct Empty {
void zero() { std::memset(this, 0, sizeof(*this)); }
};Свиду ничем не примечательный класс, который просто заполняет себя нулями. И это нормально работает для непустого класса, но пустой класс такими выкрутасами ломает соседние поля в классе, который его отнаследовал.
struct CompressedPair : Empty {
CompressedPair(Empty e, bool x) : Empty(e), b(x) { }
bool b;
};
int main() {
auto a = CompressedPair({}, true);
__builtin_printf("%b\n", a.b);
a.zero();
__builtin_printf("%b\n", a.b);
return 0;
}Выводит
1
0
(да, да, это UB, я знаю, прекратите. Мы тут поржать собрались или где?)
Но где-то тут в безумии зараждается идея... У нас же есть
no_unique_address.А на gcc он кладет данные в padding других структур.
#include <iostream>
template <typename T>
struct optional {
union {
char foo;
T val;
};
[[no_unique_address]] bool engaged;
};
template <typename View>
struct reverse_view {
[[no_unique_address]] optional<typename View::iterator> cache;
View base_;
};
struct MyView {
using iterator = int*;
char begin_;
};
int main() {
std::cout << "sizeof = " << sizeof(reverse_view<MyView>) << std::endl; // clang - 24, gcc - 16
std::cout << "alignof = " << alignof(reverse_view<MyView>) << std::endl;
}
И тут я очень хотел развернуться мыслью, но быстро нашел уже закрытый баг с std::expected. (половина примеров выше взята из обсуждения этого бага, потому что они нагляднее чем то, с чем работал я).
Если коротко, то методы expected могли зачищать переменные вокруг себя, если те оказывались в из padding. Потому что emplace пересоздавал внутренний объект целиком.
Но, увы, примера воспроизводящего это дело у меня нет. А после этого примера все остальное рядом меркнет. Увы.