Qual è lo scopo di utilizzare un'unione con un solo membro?


89

Quando stavo leggendo il codice sorgente di seastar , ho notato che esiste una struttura sindacale chiamata tx_sideche ha un solo membro. È un trucco per affrontare un certo problema?

Cordiali saluti, incollo la tx_sidestruttura qui sotto:

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


5
@MaxLanghof Questa domanda e le risposte corrispondenti non menzionavano lo scopo di utilizzare tale struttura sindacale.
daoliker,

Hai un esempio per l'uso di questo membro?
n314159,

4
Ecco perché non ho effettivamente utilizzato il mio voto stretto vincolante. Ma non sono sicuro di cosa ti aspetti esattamente dalle risposte alla tua domanda che non seguono direttamente le risposte laggiù. Presumibilmente lo scopo dell'uso unioninvece di structè una o più delle differenze tra i due. È una tecnica piuttosto oscura, quindi a meno che non arrivi l'autore originale di quel codice, non sono sicuro che qualcuno possa darti una risposta autorevole quale problema spera di risolvere con questo (se presente).
Max Langhof,

2
La mia ipotesi migliore è che l'unione sia usata per ritardare la costruzione (che è in qualche modo inutile in questo caso) o prevenire la distruzione (che porta alla perdita di memoria) di pending_fifo. Ma difficile da dire senza esempio di utilizzo.
Konstantin Stupnik,

Risposte:


82

Perché tx_sideè un'unione, tx_side()non si inizializza / costruisce automaticamente ae ~tx_side()non la distrugge automaticamente. Ciò consente un controllo accurato della durata di vita ae pending_fifo, tramite il posizionamento, di chiamate al distruttore nuove e manuali (di un uomo povero std::optional).

Ecco un esempio:

#include <iostream>

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

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

int main()
{
    B b;
}

Qui, B b;non stampa nulla, perché anon è costruito né distrutto.

Se Bfosse un struct, B()chiamerebbe A()e ~B()chiamerebbe ~A(), e non saresti in grado di impedirlo.


23
@daoliker non necessariamente casuale, ma imprevedibile da parte tua. Come qualsiasi altra variabile non inizializzata. Non puoi presumere che sia casuale; per quanto ne sai, potrebbe contenere la password dell'utente che in precedenza hai chiesto di digitare.
user253751

5
@daoliker: il commento precedente è troppo ottimista. I byte casuali avrebbero valori nell'intervallo 0-255, ma se leggi un byte non inizializzato in un intpotresti ottenere 0xCCCCCCCC. La lettura di dati non inizializzati è Comportamento indefinito e ciò che potrebbe accadere è che il compilatore ignora semplicemente il tentativo. Questa non è solo teoria. Debian ha fatto questo errore esatto e ha interrotto la sua implementazione OpenSSL. Avevano dei byte casuali reali, aggiungevano una variabile non inizializzata e il compilatore diceva "bene il risultato non è definito, quindi potrebbe anche essere zero". Zero ovviamente non è più casuale.
MSalters il

1
@MSalters: hai una fonte per questo reclamo? Perché quello che posso trovare suggerisce che non è quello che è successo: non è stato il compilatore a rimuoverlo, ma gli sviluppatori. Onestamente, sarei sorpreso se uno scrittore di compilatori prendesse una decisione così incredibilmente sbagliata. (vedi stackoverflow.com/questions/45395435/... )
Jack Aidley

5
@JackAidley: quale affermazione precisa? Hai un buon collegamento, sembra che la storia sia capovolta. OpenSSL ha sbagliato la logica e ha usato una variabile non inizializzata in modo tale che un compilatore potesse legalmente assumere qualsiasi risultato. Debian lo ha individuato correttamente, ma ha risolto il problema. Per quanto riguarda "i compilatori che prendono decisioni così sbagliate"; non prendono quella decisione. Il comportamento indefinito è una decisione sbagliata. Gli ottimizzatori sono progettati per funzionare con il codice corretto. GCC, ad esempio, non assume attivamente alcun overflow firmato. Supporre che "nessun dato non inizializzato" sia ugualmente ragionevole; può essere utilizzato per eliminare percorsi di codice impossibili.
Sali il

1
@JackAidley Ho riscontrato problemi simili a quelli menzionati da @MSalters nel mio codice; Ho erroneamente supposto che una variabile non inizializzata fosse vuota, e sono stato sconcertato quando un != 0confronto successivo ha dato la verità. Da allora ho aggiunto flag di compilazione per trattare le variabili non inizializzate come errori per assicurarmi di non cadere di nuovo in quella trappola.
Tom Lint,

0

In parole semplici, a meno che non venga assegnato / inizializzato esplicitamente un valore, l' unione a membro singolo non inizializza la memoria allocata. Questa funzionalità può essere ottenuta con std:: optionalin c ++ 17.


2
Questa è una risposta fuorviante. L'unione con un solo membro avrà le stesse dimensioni del membro. Questa memoria semplicemente non verrà inizializzata fino a quando il membro non verrà inizializzato.
Kirill Dmitrenko il
Utilizzando il nostro sito, riconosci di aver letto e compreso le nostre Informativa sui cookie e Informativa sulla privacy.
Licensed under cc by-sa 3.0 with attribution required.