Apa tujuan menggunakan serikat dengan hanya satu anggota?

89

Ketika saya membaca kode sumber seastar , saya perhatikan bahwa ada struktur gabungan tx_sideyang hanya memiliki satu anggota. Apakah ini beberapa hack untuk menangani masalah tertentu?

FYI, saya tempel tx_sidestruktur di bawah ini:

union tx_side {
    tx_side() {}
    ~tx_side() {}
    void init() { new (&a) aa; }
    struct aa {
        std::deque<work_item*> pending_fifo;
    } a;
} _tx;
daoliker
sumber
1
Duplikat potensial stackoverflow.com/questions/26572432/… .
Max Langhof
5
@ MaxLanghof Pertanyaan ini dan jawaban yang sesuai tidak menyebutkan tentang tujuan menggunakan struktur serikat tersebut.
daoliker
Apakah Anda memiliki contoh untuk penggunaan anggota ini?
n314159
4
Itu sebabnya saya tidak benar-benar menggunakan suara dekat saya yang mengikat. Tetapi saya tidak yakin apa yang sebenarnya Anda harapkan dari jawaban atas pertanyaan Anda yang tidak mengikuti langsung dari jawaban di sana. Agaknya tujuan penggunaan unionbukannya structsatu atau lebih perbedaan antara keduanya. Ini adalah teknik yang sangat tidak jelas sehingga kecuali penulis asli kode itu datang, saya tidak yakin seseorang dapat memberi Anda jawaban otoritatif yang masalah mereka berharap untuk menyelesaikan dengan ini (jika ada).
Max Langhof
2
Tebakan terbaik saya adalah bahwa penyatuan digunakan untuk menunda konstruksi (yang agak tidak berguna dalam kasus ini) atau mencegah kerusakan (yang menyebabkan kebocoran memori) dari pending_fifo. Namun sulit dikatakan tanpa contoh penggunaan.
Konstantin Stupnik

Jawaban:

82

Karena tx_sideadalah suatu kesatuan, tx_side()tidak secara otomatis menginisialisasi / membangun a, dan ~tx_side()tidak secara otomatis merusaknya. Hal ini memungkinkan kontrol dengan baik selama masa pakai adan pending_fifo, melalui penempatan destruktor-baru dan manual (orang miskin std::optional).

Ini sebuah contoh:

#include <iostream>

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

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

int main()
{
    B b;
}

Di sini, B b;tidak mencetak apa pun, karena atidak dibangun atau dihancurkan.

Jika Bitu adalah struct, B()akan memanggil A(), dan ~B()akan memanggil ~A(), dan Anda tidak akan dapat mencegahnya.

HolyBlackCat
sumber
23
@daoliker tidak harus acak, tetapi tidak dapat Anda prediksi. Sama seperti variabel tidak diinisialisasi lainnya. Anda tidak dapat menganggap itu acak; untuk semua yang Anda tahu itu bisa menampung kata sandi pengguna yang sebelumnya Anda minta mereka ketik.
user253751
5
@daoliker: Komentar sebelumnya terlalu optimis. Bytes acak akan memiliki nilai dalam kisaran 0-255, tetapi jika Anda membaca byte yang tidak diinisialisasi menjadi intAnda mungkin mendapatkan 0xCCCCCCCC. Membaca data yang tidak diinisialisasi adalah Perilaku Tidak Terdefinisi, dan apa yang mungkin terjadi adalah bahwa kompiler hanya membuang upaya. Ini bukan hanya teori. Debian membuat kesalahan yang tepat ini, dan itu merusak implementasi OpenSSL mereka. Mereka memiliki beberapa byte acak nyata, menambahkan variabel tidak diinisialisasi, dan kompiler mengatakan "baik hasilnya tidak terdefinisi, jadi mungkin juga nol". Nol jelas tidak acak lagi.
MSalters
1
@MSalters: Apakah Anda memiliki sumber untuk klaim ini? Karena apa yang dapat saya temukan menunjukkan bahwa bukan itu yang terjadi: bukan kompiler yang menghapusnya, tetapi pengembang. Jujur, saya akan kagum jika ada penulis kompiler membuat keputusan yang sangat buruk. (lihat stackoverflow.com/questions/45395435/… )
Jack Aidley
5
@JackAidley: Klaim mana yang tepat? Anda memang memiliki tautan yang bagus, sepertinya kisah saya terbalik. OpenSSL salah logikanya, dan menggunakan variabel yang tidak diinisialisasi sedemikian rupa sehingga kompiler dapat secara legal mengasumsikan hasil apa pun. Debian dengan benar melihat itu, tetapi memecahkan perbaikannya. Adapun "kompiler membuat keputusan buruk"; mereka tidak membuat keputusan itu. Perilaku Tidak Terdefinisi adalah keputusan yang buruk. Pengoptimal dirancang untuk berjalan pada kode yang benar. GCC misalnya aktif mengasumsikan tidak ada luapan yang ditandatangani. Dengan asumsi "tidak ada data yang tidak diinisialisasi" sama masuk akal; dapat digunakan untuk menghilangkan jalur kode yang tidak mungkin.
MSalters
1
@JackAidley Saya pernah mengalami masalah yang mirip dengan apa yang @Malters sebutkan dalam kode saya sendiri; Saya keliru mengasumsikan variabel yang tidak diinisialisasi akan kosong, dan bingung ketika != 0perbandingan berikutnya menghasilkan true. Saya telah menambahkan flag kompiler untuk memperlakukan variabel yang tidak diinisialisasi sebagai kesalahan untuk memastikan saya tidak akan jatuh ke dalam perangkap itu lagi.
Tom Lint
0

Dengan kata-kata sederhana, kecuali jika secara eksplisit menetapkan / menginisialisasi nilai, anggota tunggal tidak menginisialisasi memori yang dialokasikan. Fungsi ini dapat dicapai dengan std:: optionaldi c ++ 17.

Sitesh
sumber
2
Itu jawaban yang menyesatkan. Persatuan dengan hanya satu anggota akan memiliki ukuran yang sama dengan anggota. Memori ini tidak akan diinisialisasi sampai anggota diinisialisasi.
Kirill Dmitrenko