I have a union that looks like this:
union {
int intValue;
double doubleValue;
std::string stringValue;
void *pointerValue;
} values;
When I compile it, I get this error message (yes, I did #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);
^
I compile it using this command:
$ clang++ *.cc -isysroot /Developer/SDKs/MacOSX10.7.sdk/ -shared
How can I use an std::string
in a union?
You cannot.
A union combines two pieces of functionality: the ability to store an object which may be of a select number of types, and the ability to effectively (and implementation-defined) convert between those types. You could put an integer in and look at its representation as a double. And so forth.
Because a union must support both of these pieces of functionality (and for a few other reasons, like being able to construct one), a union prevents you from doing certain things. Namely, you cannot put "live" objects in them. Any object that is "living" enough that it needs a non-default copy constructor (among many other restrictions) cannot be a member of a union.
After all, a union object does not really have the concept of which type of data it actually stores. It does not store one type of data; it stores all of them, at the same time. It is up to you to be able to fish the right type out. So how could it reasonably copy one union value into another?
Members of a union must be a POD (plain-old-data) type. And while C++11 does loosen those rules, objects still must have a default (or otherwise trivial) copy constructor. And std::string
's copy constructor is non-trivial.
What you likely want is a boost::variant
. That is an object that can store a number of possible types, just like a union. Unlike a union however, it is type-safe. It therefore knows what is actually in the union; it is therefore able to copy itself and otherwise behave like a regular C++ object.
You cannot put a std::string
in a union. It is disallowed by the C++ language because it is unsafe. Consider that most std::string
implementations have a pointer to some dynamic memory that holds the value of the string. Consider also that there is no way to know which union member is currently active.
An implementation cannot call the destructor of std::string
, because it does not know that the std::string
object is the currently active member, but if it does not call the destructor then memory will be leaked.
As per the C++ Standard §9.5.1:
An object of a class with a non-trivial constructor, a non-trivial copy constructor, a non-trivial destructor, or a non-trivial copy assignment operator cannot be a member of a union.
Hence, members of a union can't have constructors, destructors, virtual member functions, or base classes. Hence you cannot use std::string as an member of union.
Alternative Solution:
You can use boost::variant or boost::any.
Unfortunately, you cannot use non-POD (plain old data) types in a union. A fairly typical and simple workaround for this is to wrap the union in a struct, and move the non-POD instance from inside the union to the struct.
For example:
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";
You do however pay a price for this - the memory footprint of the Value
struct will be larger than that of the original union.
union
cannot have member of following types §9.5/1:
An object of a class with a non-trivial constructor (12.1), a non-trivial copy constructor (12.8), a non-trivial destructor (12.4), or a non-trivial copy assignment operator (13.5.3, 12.8) cannot be a member of a union, nor can an array of such objects.
So either you define a pointer to std::string as:
union {
int intValue;
double doubleValue;
std::string *stringValue; //pointer
void *pointerValue;
} values;
or, use boost union which is known as Boost.Variant
精彩评论