TGViewer
Грокаем C++ Грокаем C++ @grokaemcpp · 9.35K subscribers
Post #1005 4.3K
​​Шо там не так с union?
#опытным

Объединения в С++, мягко говоря, не любят. И сегодня мы обсудим почему.

Юнион - это такой специальный класс, который хранит только одно из своих полей. Любое, но одно. Объект объединения может хранить разные поля в одной и той же памяти. А размер union равен размеру максимального поля.

union U {
int integer;
float floating;
char character;
};

U u;
u.integer = 42; // now u hold int
u.floating = 3.14; // now u hold float
u.character = 'a'; // now u hold char


U может хранить или целое число, или число с плавающей точкой, или символ. Последнее записанное поле становится "активным" полем.

Объединение позволяет, например, хранить несвязанные типы в одном массиве.

Но с юнионами нужно быть очень осторожным. Вот представьте, вам на вход функции пришел массив объединений. Как понять, какое реально поле в нем лежит?

Никак. Такого функционала нет. Поэтому приходится делать разные обвязки:

struct Helper {
enum class Type { Int, Float, Char };

union U {
int integer;
float floating;
char character;
};

Type objectType;
U value;

Helper(int i) : objectType(Type::Int), value{.integer = i} {}
Helper(float f) : objectType(Type::Float), value{.floating = f} {}
Helper(char c) : objectType(Type::Char), value{.character = c} {}
};


При создании объекта Helper мы теперь явно перечислением задаем тип, который лежит в юнион. И все кажется безопасным, пока мы не сделаем ошибку:

Helper h{42};

h.value.floating = 3.14;


Мы изменили активное поле без изменения objectType. А теперь где-то в другом месте кода:

if (h.objectType == Helper::Type::Int) {
std::cout << h.value.integet << std::endl;
}


Вот и все. Мы вышли на кривую дорожку неопределенного поведения.

Доступ до неактивного члена объединения - UB.

То есть. Мало того, что эта штука не самостоятельная. Так еще и если мы набагали, то это может обернуться вагоном трудноотловимых проблем в рантайме.

По сути, объединениями можно пользоваться, если руки из нужного места растут. Но если с руками не повезло - расстрел. И вы будете долго ковыряться в кишках проекта с надеждой хоть на какую-то подсказку о том, что произошло.

В С++ давно уже есть прекрасная типобезопасная альтернатива - std::variant. При доступе не к тому типу он хотя бы выкинет исключение std::bad_variant_access. И будет понятно хотя бы, где собака зарыта. Точнее не всегда понятно где конкретно, но точно понятно, что зарыта именно собака. А это уже облегчает поиски.

Be safe. Stay cool.

#cppcore
  • ❤ 29
  • 🔥 12
  • 👍 11
More from @grokaemcpp
  1. Oct 12, 2026​​WAT #новичкам Как вы думаете, результат выражения: bool math = -1 < 1U; будет правдой ил…
  2. Oct 8, 2026​​Strict weak ordering #опытным На первый взгляд, всё выглядит рабочим: мы создаём 40 зака…
  3. Oct 7, 2026​​Где-то баг... #опытным Вот вам код: struct Order { int price; int id; }; int main() { st…
  4. Oct 5, 2026Откуда spurious wakeup на кондваре? #опытным У кондваров есть метод std::condition_variabl…
  5. Oct 1, 2026​​Stacktrace. Tips #опытным Чтобы полноценно работать со стандартными трейсами, нужно знат…
  6. Sep 28, 2026​​Stacktrace #опытным Одна из проблема исключений - непонятно, откуда оно прилетело. Ну да…
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 →