Was ist der Zweck einer Gewerkschaft mit nur einem Mitglied?


89

Als ich Seastar-Quellcode las , bemerkte ich, dass es eine Gewerkschaftsstruktur namens gibt, tx_sidedie nur ein Mitglied hat. Ist das ein Hack, um ein bestimmtes Problem zu lösen?

Zu Ihrer Information, ich füge die folgende tx_sideStruktur ein:

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


5
@MaxLanghof In dieser Frage und den entsprechenden Antworten wurde der Zweck der Verwendung einer solchen Gewerkschaftsstruktur nicht erwähnt.
Daoliker

Haben Sie ein Beispiel für die Verwendung dieses Mitglieds?
n314159

4
Deshalb habe ich meine verbindliche Abstimmung nicht wirklich genutzt. Aber ich bin mir nicht sicher, was genau Sie von Antworten auf Ihre Frage erwarten, die nicht direkt aus den Antworten dort folgen. Vermutlich ist der Zweck der Verwendung unionanstelle von structeiner oder mehrere der Unterschiede zwischen den beiden. Es ist eine ziemlich obskure Technik. Wenn also nicht der ursprüngliche Autor dieses Codes mitkommt, bin ich mir nicht sicher, ob Ihnen jemand eine maßgebliche Antwort geben kann, welches Problem er damit lösen möchte (falls vorhanden).
Max Langhof

2
Ich gehe davon aus, dass union verwendet wird, um entweder die Konstruktion zu verzögern (was in diesem Fall etwas sinnlos ist) oder die Zerstörung (die zu einem Speicherverlust führt) von pending_fifo zu verhindern. Aber ohne Anwendungsbeispiel schwer zu sagen.
Konstantin Stupnik

Antworten:


82

Weil tx_sidees sich um eine Union handelt, tx_side()die nicht automatisch initialisiert / konstruiert aund ~tx_side()nicht automatisch zerstört wird. Dies ermöglicht eine fein abgestimmte Kontrolle über die Lebensdauer von aund pending_fifoüber platzierungsneue und manuelle Destruktoraufrufe (eines armen Mannes)std::optional ).

Hier ist ein Beispiel:

#include <iostream>

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

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

int main()
{
    B b;
}

Hier B b;druckt nichts, weila weder konstruiert noch zerstört wird.

Wenn wäre Bein struct, B()würde anrufen A()und ~B()würde anrufen ~A(), und Sie könnten das nicht verhindern.


23
@daoliker nicht unbedingt zufällig, aber für dich unvorhersehbar. Wie jede andere nicht initialisierte Variable. Sie können nicht annehmen, dass es zufällig ist;
Nach

5
@daoliker: Der vorherige Kommentar ist zu optimistisch. Zufällige Bytes hätten Werte im Bereich von 0 bis 255, aber wenn Sie ein nicht initialisiertes Byte in ein lesen, erhalten intSie möglicherweise 0xCCCCCCCC. Das Lesen nicht initialisierter Daten ist undefiniertes Verhalten, und es kann vorkommen, dass der Compiler den Versuch einfach verwirft. Dies ist nicht nur Theorie. Debian hat genau diesen Fehler gemacht und die OpenSSL-Implementierung unterbrochen. Sie hatten einige echte zufällige Bytes, fügten eine nicht initialisierte Variable hinzu und der Compiler sagte: "Nun, das Ergebnis ist undefiniert, also könnte es genauso gut Null sein." Null ist offensichtlich nicht mehr zufällig.
MSalters

1
@ MSalters: Haben Sie eine Quelle für diesen Anspruch? Denn was ich finden kann, deutet darauf hin, dass dies nicht der Fall ist: Es war nicht der Compiler, der es entfernt hat, sondern die Entwickler. Ehrlich gesagt wäre ich erstaunt, wenn ein Compiler-Autor eine so unglaublich schlechte Entscheidung treffen würde. (siehe stackoverflow.com/questions/45395435/… )
Jack Aidley

5
@ JackAidley: Welche genaue Behauptung? Sie haben eine gute Verbindung, anscheinend habe ich die Geschichte umgekehrt. OpenSSL hat die Logik falsch verstanden und eine nicht initialisierte Variable so verwendet, dass ein Compiler legal jedes Ergebnis annehmen kann. Debian hat das richtig erkannt, aber das Problem behoben. Was "Compiler betrifft, die solche schlechten Entscheidungen treffen"; Sie treffen diese Entscheidung nicht. Das undefinierte Verhalten ist die schlechte Entscheidung. Optimierer sind so konzipiert, dass sie mit korrektem Code ausgeführt werden. GCC geht beispielsweise aktiv von keinem signierten Überlauf aus. Die Annahme, dass "keine nicht initialisierten Daten" vorliegen, ist ebenso vernünftig. Es kann verwendet werden, um unmögliche Codepfade zu beseitigen.
MSalters

1
@JackAidley Ich bin auf ähnliche Probleme gestoßen wie @MSalters in meinem eigenen Code. Ich habe fälschlicherweise angenommen, dass eine nicht initialisierte Variable leer ist, und war verblüfft, als ein nachfolgender != 0Vergleich wahr ergab. Seitdem habe ich Compiler-Flags hinzugefügt, um nicht initialisierte Variablen als Fehler zu behandeln und sicherzustellen, dass ich nicht wieder in diese Falle tappe.
Tom Lint

0

Mit einfachen Worten, wenn nicht explizit ein Wert zugewiesen / initialisiert wird, initialisiert die Vereinigung einzelner Mitglieder den zugewiesenen Speicher nicht. Diese Funktionalität kann mit std:: optionalin c ++ 17 erreicht werden.


2
Das ist eine irreführende Antwort. Eine Union mit nur einem Mitglied hat die gleiche Größe wie das Mitglied. Dieser Speicher wird einfach erst initialisiert, wenn das Mitglied initialisiert ist.
Kirill Dmitrenko
Durch die Nutzung unserer Website bestätigen Sie, dass Sie unsere Cookie-Richtlinie und Datenschutzrichtlinie gelesen und verstanden haben.
Licensed under cc by-sa 3.0 with attribution required.