Mục đích của việc sử dụng một liên minh chỉ có một thành viên là gì?


89

Khi tôi đang đọc mã nguồn seastar , tôi nhận thấy rằng có một cấu trúc hợp nhất được gọi là tx_sidechỉ có một thành viên. Đây có phải là một số hack để đối phó với một vấn đề nhất định?

FYI, tôi dán tx_sidecấu trúc bên dưới:

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

1
Bản sao tiềm năng của stackoverflow.com/questions/26572432/ .
Max Langhof

5
@MaxLanghof Câu hỏi này và câu trả lời tương ứng không đề cập đến mục đích sử dụng cấu trúc liên minh như vậy.
daoliker

Bạn có một ví dụ cho việc sử dụng thành viên này?
n314159

4
Đó là lý do tại sao tôi không thực sự sử dụng phiếu bầu đóng ràng buộc của mình. Nhưng tôi không chắc chính xác những gì bạn mong đợi từ câu trả lời cho câu hỏi của bạn mà không theo dõi trực tiếp từ câu trả lời ở đó. Có lẽ mục đích của việc sử dụng unionthay vì structlà một hoặc nhiều sự khác biệt giữa hai. Đây là một kỹ thuật khá khó hiểu vì vậy trừ khi tác giả ban đầu của mã đó xuất hiện, tôi không chắc ai đó có thể đưa ra cho bạn câu trả lời có thẩm quyền mà họ hy vọng sẽ giải quyết vấn đề này (nếu có).
Max Langhof

2
Dự đoán tốt nhất của tôi là liên minh được sử dụng để trì hoãn việc xây dựng (điều này hơi vô nghĩa trong trường hợp này) hoặc ngăn chặn sự phá hủy (dẫn đến rò rỉ bộ nhớ) của cấp phát_fifo. Nhưng khó có thể nói mà không có ví dụ về việc sử dụng.
Konstantin Stupnik

Câu trả lời:


82

Bởi vì tx_sidelà một liên minh, tx_side()không tự động khởi tạo / xây dựng a~tx_side()không tự động phá hủy nó. Điều này cho phép kiểm soát chi tiết trong suốt vòng đời apending_fifo, thông qua các cuộc gọi hủy diệt thủ công và mới (vị trí của một người nghèo std::optional).

Đây là một ví dụ:

#include <iostream>

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

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

int main()
{
    B b;
}

Ở đây, B b;in không có gì, vì akhông được xây dựng cũng không bị phá hủy.

Nếu Blà một struct, B()sẽ gọi A()~B()sẽ gọi ~A(), và bạn sẽ không thể ngăn chặn điều đó.


23
@daoliker không nhất thiết là ngẫu nhiên, nhưng bạn không thể đoán trước được. Giống như bất kỳ biến chưa được khởi tạo khác. Bạn không thể cho rằng nó ngẫu nhiên; vì tất cả những gì bạn biết nó có thể giữ mật khẩu người dùng mà trước đây bạn đã yêu cầu họ nhập.
user253751

5
@daoliker: Nhận xét trước quá lạc quan. Các byte ngẫu nhiên sẽ có các giá trị trong phạm vi 0-255, nhưng nếu bạn đọc một byte chưa được khởi tạo thành một byte intbạn có thể nhận được 0xCCCCCCCC. Đọc dữ liệu chưa được khởi tạo là Hành vi không xác định và điều có thể xảy ra là trình biên dịch chỉ cần loại bỏ nỗ lực. Đây không chỉ là lý thuyết. Debian đã mắc lỗi chính xác này và nó đã phá vỡ triển khai OpenSSL của họ. Họ đã có một số byte ngẫu nhiên thực sự, thêm một biến chưa được khởi tạo và trình biên dịch cho biết "kết quả không được xác định rõ ràng, vì vậy nó cũng có thể bằng không". Zero rõ ràng không còn ngẫu nhiên nữa.
MSalters

1
@MSalters: Bạn có nguồn nào cho khiếu nại này không? Bởi vì những gì tôi có thể tìm thấy cho thấy đó không phải là những gì đã xảy ra: đó không phải là trình biên dịch đã loại bỏ nó, mà là các nhà phát triển. Thành thật mà nói, tôi sẽ ngạc nhiên nếu bất kỳ nhà văn biên dịch nào đưa ra một quyết định cực kỳ tồi tệ như vậy. (xem stackoverflow.com/questions/45395435/
Jack Aidley

5
@JackAidley: Yêu cầu chính xác nào? Bạn có một liên kết tốt, dường như tôi đã đảo ngược câu chuyện. OpenSSL đã sai logic và đã sử dụng một biến chưa được khởi tạo theo cách mà trình biên dịch có thể thừa nhận một cách hợp pháp bất kỳ kết quả nào. Debian phát hiện chính xác điều đó, nhưng đã phá vỡ bản sửa lỗi. Đối với "trình biên dịch đưa ra quyết định tồi tệ như vậy"; họ không đưa ra quyết định đó. Hành vi không xác định là quyết định tồi. Tối ưu hóa được thiết kế để chạy trên mã chính xác. GCC chẳng hạn, chủ động cho rằng không có tràn ký kết. Giả sử "không có dữ liệu chưa được khởi tạo" là hợp lý như nhau; nó có thể được sử dụng để loại bỏ các đường dẫn mã không thể.
MSalters

1
@JackAidley Tôi đã gặp phải các vấn đề tương tự như những gì @MSalters đề cập đến trong mã của riêng tôi; Tôi đã giả định sai một biến chưa được khởi tạo sẽ trống và bị cản trở khi một != 0so sánh tiếp theo cho kết quả đúng. Kể từ khi tôi thêm các cờ trình biên dịch để coi các biến chưa được khởi tạo là lỗi để đảm bảo tôi sẽ không rơi vào bẫy đó nữa.
Tom Lint

0

Nói một cách đơn giản, trừ khi được gán / khởi tạo một giá trị rõ ràng, một liên kết thành viên không khởi tạo bộ nhớ được phân bổ. Chức năng này có thể đạt được với std:: optionaltrong c ++ 17.


2
Đó là một câu trả lời sai lệch. Liên minh chỉ có một thành viên sẽ có cùng quy mô với thành viên. Bộ nhớ này đơn giản sẽ không được khởi tạo cho đến khi thành viên được khởi tạo.
Kirill Dmitrenko
Khi sử dụng trang web của chúng tôi, bạn xác nhận rằng bạn đã đọc và hiểu Chính sách cookieChính sách bảo mật của chúng tôi.
Licensed under cc by-sa 3.0 with attribution required.