TGViewer
C++: Хроники Дурки🚑 C++: Хроники Дурки🚑 @cpp_durka · 916 subscribers
Post #74 1.31K
Ладно, искать баги в компиляторах весело, но недостаточно.

Давайте поиграемся в чуть более интересные игры.

Вспомним, что у нас в С++ есть правило: структура не может иметь нулевой размер. Потому что у ее инстанса должен быть адрес, и здесь С++ как-то забыл про 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 пересоздавал внутренний объект целиком.


Но, увы, примера воспроизводящего это дело у меня нет. А после этого примера все остальное рядом меркнет. Увы.
godbolt.org Compiler Explorer - C++ 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 …
  • ❤ 15
  • 🔥 2
  • 💊 1
More from @cpp_durka
  1. Sep 8, 2026Мошенники заставили пенсионерку из Москвы переписать квартиру на Rust
  2. Aug 31, 2026Всяко разное болезненное есть в С++, из всего болезненного одно из самых болезненных - это…
  3. Aug 24, 2026Если кто не знаком с библиотекой nlohmann/json - она прекрасна. Мои мысли о том, как должн…
  4. Aug 21, 2026#толькосвоимемы Код взят тут. if (dim == 0) idx_dim = i0; else if (dim == 1) idx_dim = i1;…
  5. Aug 10, 2026Ладно, разумеется прошлый пост был набросом. Никогда не обновляйте компиляторы без очень с…
  6. Aug 3, 2026Убедительная просьба - обновляйте компиляторы. Помимо внедрения множества способов выстрел…
Threads Profile ViewerView any public Threads profile without an account.Open ThreadLook →Writing with AI? Make it sound human.Metric37 rewrites AI drafts so they read naturally. Free AI detector, 1,500 words free.Try Metric37 →