Яка мета використання союзу лише з одним членом?


89

Коли я читав вихідний код моря , я помітив, що існує структура союзу, tx_sideяка має лише одного члена. Це якийсь хакер для вирішення певної проблеми?

FYI, я вставляю tx_sideструктуру нижче:

union tx_side {
    tx_side() {}
    ~tx_side() {}
    void init() { new (&a) aa; }
    struct aa {
        std::deque<work_item*> pending_fifo;
    } a;
} _tx;

1
Потенційний дублікат stackoverflow.com/questions/26572432 / ... .
Макс Лангхоф

5
@MaxLanghof У цьому запитанні та відповідних відповідях не було сказано про мету використання такої структури об'єднання.
даолікер

Чи є у вас приклад для використання цього члена?
n314159

4
Ось чому я насправді не використовував своє обов'язкове закрите голосування. Але я не впевнений, чого саме ви очікуєте від відповідей на ваше запитання, що не випливає безпосередньо з відповідей там. Імовірно, метою використання unionзамість цього structє одна або кілька відмінностей між ними. Це досить незрозуміла техніка, тому, якщо не прийде оригінальний автор цього коду, я не впевнений, що хтось може дати тобі авторитетну відповідь, яку проблему вони сподіваються вирішити з цим (якщо така є).
Макс Ленгоф

2
Я найкраще здогадуюсь, що союз використовується або для затримки побудови (що в цьому випадку дещо безглуздо), або запобігання руйнуванню (що призводить до витоку пам'яті) pending_fifo. Але важко сказати без прикладу використання.
Костянтин Ступник

Відповіді:


82

Оскільки tx_sideє об'єднанням, tx_side()він не ініціалізує / конструює автоматично aі ~tx_side()не знищує його автоматично. Це дозволяє тонкозернистий контроль за терміном експлуатації aта pending_fifoза допомогою нових та ручних викликів деструкторів (бідних std::optional).

Ось приклад:

#include <iostream>

struct A
{
    A() {std::cout << "A()\n";}
    ~A() {std::cout << "~A()\n";}
};

union B
{
    A a;
    B() {}
    ~B() {}
};

int main()
{
    B b;
}

Тут B b;нічого не друкується, бо aне побудовано і не зруйновано.

Якби Bбув a struct, B()зателефонував би A()і ~B()зателефонував ~A(), а ви не змогли б цього запобігти.


23
@daoliker не обов'язково випадковий, але непередбачуваний для вас. Те саме, що і будь-яка інша неініціалізована змінна. Ви не можете припустити, що це випадково; тому що ви знаєте, що він може містити пароль користувача, який ви попередньо просили їх ввести.
user253751

5
@daoliker: Попередній коментар занадто оптимістичний. Випадкові байти мали б значення в діапазоні 0-255, але якщо ви прочитаєте неініціалізований байт у intви можете отримати 0xCCCCCCCC. Читання неініціалізованих даних - це не визначена поведінка, і що може статися, що компілятор просто відкидає спробу. Це не просто теорія. Debian допустив цю точну помилку, і це порушило їхню реалізацію OpenSSL. Вони мали деякі реальні випадкові байти, додали неініціалізовану змінну, і компілятор сказав: "ну результат не визначений, тому він може бути нульовим". Нуль, очевидно, більше не випадковий.
MSalters

1
@MSalters: У вас є джерело для цієї претензії? Тому що те, що я можу знайти, говорить про те, що це не сталося: його видалив не компілятор, а розробники. Чесно кажучи, я був би здивований, якби будь-який автор-упорядник прийняв таке неймовірно погане рішення. (Див stackoverflow.com/questions/45395435 / ... )
Jack Aidley

5
@JackAidley: Яка точна претензія? У вас є гарне посилання, здається, я перевернув історію. OpenSSL помилився з логікою та використав неініціалізовану змінну таким чином, що компілятор міг юридично припустити будь-який результат. Debian це правильно помітив, але зламав виправлення. Щодо "компіляторів, які приймають такі погані рішення"; вони не приймають такого рішення. Невизначена поведінка - це погане рішення. Оптимізатори розроблені для роботи за правильним кодом. Наприклад, GCC не передбачає жодного підписаного переповнення. Припускати, що "відсутні неініціалізовані дані" не менш розумно; його можна використовувати для усунення неможливих кодових шляхів.
MSalters

1
@JackAidley У мене виникли подібні проблеми з тим, що згадує @MSalters у власному коді; Я помилково припустив, що неініціалізована змінна буде порожньою, і вона була збита з глузду, коли подальше != 0порівняння дало істину. З тих пір я додав прапори компілятора для трактування неініціалізованих змінних як помилок, щоб переконатися, що я знову не потрапляю в цю пастку.
Том Лінт

0

Простими словами, якщо явно не присвоєно / ініціалізовано значення, об'єднання одного члена не ініціалізує виділену пам'ять. Цю функціональність можна досягти за допомогою std:: optionalc ++ 17.


2
Це хибна відповідь. Союз лише з одним членом матиме такий же розмір, що і член. Ця пам'ять просто не буде ініціалізована, поки член не буде ініціалізований.
Кирило Дмитренко
Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.