2011-09-04 29 views
18

Tôi có một đoàn trông như thế này:thành viên Liên minh có một bản sao constructor không tầm thường

union { 
    int intValue; 
    double doubleValue; 
    std::string stringValue; 
    void *pointerValue; 
} values; 

Khi tôi biên dịch nó, tôi nhận được thông báo lỗi này (vâng, tôi đã làm #include <string>):

./Value.hh:19:19: error: union member 'stringValue' has a non-trivial copy constructor     
     std::string stringValue;                   
       ^                     
/Developer/SDKs/MacOSX10.7.sdk//usr/include/c++/4.2.1/bits/basic_string.h:434:7: note: because   
     type 'std::basic_string<char>' has a user-declared copy constructor        
     basic_string(const basic_string& __str);               
    ^

tôi biên dịch nó sử dụng lệnh này:

$ clang++ *.cc -isysroot /Developer/SDKs/MacOSX10.7.sdk/ -shared 

Làm thế nào tôi có thể sử dụng một std::string trong một liên minh?

+0

Bạn thực sự có thể không muốn sử dụng liên minh. –

Trả lời

22

Bạn không thể.

Công đoàn kết hợp hai phần chức năng: khả năng lưu trữ một đối tượng có thể là một số loại được chọn và khả năng chuyển đổi hiệu quả (và được xác định theo cách thực hiện) giữa các loại đó. Bạn có thể đặt một số nguyên vào và nhìn vào biểu diễn của nó như là một đôi. Và kể từ đó trở đi.

Vì công đoàn phải hỗ trợ cả hai các chức năng này (và vì một vài lý do khác, như có thể xây dựng một chức năng), công đoàn ngăn bạn làm những việc nhất định. Cụ thể, bạn không thể đặt các đối tượng "sống" vào chúng. Bất kỳ đối tượng nào "sống" đủ để nó cần một hàm tạo bản sao không mặc định (trong số nhiều hạn chế khác) không thể là thành viên của một liên minh.

Sau khi tất cả, một đối tượng công đoàn không thực sự có khái niệm về loại dữ liệu mà nó thực sự lưu trữ. Nó không lưu trữ một loại dữ liệu; nó lưu trữ tất cả trong số đó, cùng một lúc. Bạn có thể đánh cá đúng loại. Vậy làm thế nào nó có thể sao chép hợp lý một giá trị công đoàn thành một giá trị khác?

Thành viên của công đoàn phải là loại POD (thuần cũ-dữ liệu). Và trong khi C++ 11 làm nới lỏng các quy tắc đó, các đối tượng vẫn phải có một hàm tạo bản sao mặc định (hoặc khác thường). Và xây dựng bản sao của std::string là không tầm thường.

Điều bạn có thể muốn là boost::variant. Đó là một đối tượng có thể lưu trữ một số loại có thể, giống như một công đoàn. Tuy nhiên, không giống như một công đoàn, nó là loại an toàn. Do đó, nó biết những gì thực sự trong công đoàn; do đó nó có thể sao chép chính nó và nếu không hành xử giống như một đối tượng C++ thông thường.

5

Bạn không thể đặt std::string trong liên minh. Nó không được phép sử dụng ngôn ngữ C++ vì nó không an toàn. Hãy xem xét rằng hầu hết các triển khai std::string đều có con trỏ đến một số bộ nhớ động giữ giá trị của chuỗi. Cũng nên xem xét rằng không có cách nào để biết thành viên công đoàn nào hiện đang hoạt động.

Việc triển khai không thể gọi hàm hủy là std::string, bởi vì nó không biết đối tượng std::string là thành viên hiện đang hoạt động, nhưng nếu nó không gọi hàm hủy thì bộ nhớ sẽ bị rò rỉ.

+3

Tôi tin rằng trong C++ 11 bạn được phép đặt các kiểu với các nhà xây dựng/destruct không tầm thường trong một công đoàn, nhưng bạn phải gọi chúng một cách rõ ràng với cú pháp vị trí mới và t. ~ T(). 9.5.3-9.5.4 –

1

union không thể có thành viên của loại sau đây §9.5/1:

Một đối tượng của một lớp với một constructor không tầm thường (12.1), một constructor sao chép không tầm thường (12,8), một phi phá hủy tầm thường (12.4), hoặc một toán tử gán bản sao không tầm thường (13.5.3, 12.8) không thể là thành viên của một liên minh, cũng không thể là một mảng của các đối tượng như vậy.

Vì vậy, hoặc là bạn định nghĩa một con trỏ đến std :: string như:

union { 
    int intValue; 
    double doubleValue; 
    std::string *stringValue; //pointer 
    void *pointerValue; 
} values; 

hay, sử dụng tăng đoàn được gọi là Boost.Variant

+0

Bạn có nhớ xem nhận xét của tôi về câu trả lời của Alok không (để tránh lặp lại bản thân) và cho tôi biết bất kỳ ý tưởng nào? –

3

Theo C++ chuẩn §9.5.1:

Một đối tượng của một lớp với một nhà xây dựng không tầm thường, một nhà xây dựng bản sao không tầm thường, một destruct không tầm thường hoặc một nhà điều hành gán bản sao không tầm thường không thể là thành viên của au nion.

Do đó, các thành viên của liên minh không thể có nhà thầu, hàm hủy, hàm thành viên ảo hoặc lớp cơ sở. Do đó bạn không thể sử dụng std :: string như một thành viên của union.

Giải pháp thay thế:

Bạn có thể sử dụng boost :: biến hoặc boost :: bất kỳ.

+0

Dường như tôi cần phải điều tra lý do tại sao tôi có thể đặt một lớp với một bản sao tùy chỉnh gán 'toán tử = (T const & đó)' vào một 'công đoàn' tốt ... nhiều lần hơn. Hạn chế này được dỡ bỏ trong C++ 14, hay là 'union'' rơi ngược 'sang cái khác, ngầm định gán, _e.g._ 'operator = (T đó)'? –

+0

^Tôi tin rằng ở trên chỉ là trình biên dịch không đủ nghiêm khắc với tôi. Bây giờ tôi tránh hình mẫu đó: không ai trong số các thành viên 'đoàn kết 'của tôi có những thứ họ không nên làm. Và tất cả họ vẫn làm việc tuyệt vời. :) –

2

Thật không may, bạn không thể sử dụng các loại không phải POD (dữ liệu cũ) trong một công đoàn. Một cách giải quyết khá đơn giản và đơn giản cho việc này là bọc liên kết trong một cấu trúc, và di chuyển cá thể không phải POD từ bên trong liên kết đến cấu trúc.

Ví dụ:

struct Value { 
    union { 
     int intValue; 
     double doubleValue; 
     void *pointerValue; 
    }; 
    std::string stringValue; 
}; 

Value value; 

// Demonstration of accessing members: 

value.intValue = 0; 
value.doubleValue = 0.0; 
value.pointerValue = NULL; 
value.stringValue = "foo"; 

Bạn tuy nhiên phải trả giá cho điều này - bộ nhớ của Value struct sẽ lớn hơn so với các công đoàn gốc.

Các vấn đề liên quan