为什么编译器不允许std :: string内联合?

我想在Union中使用string。 如果我写如下

union U { int i; float f; string s; }; 

编译器给出错误说U :: S有复制构造函数。

我读了一些其他的post来解决这个问题。 但我想知道为什么编译器不允许这个在第一位?

编辑:@KennyTM:在任何联盟,如果成员初始化其他人将有垃圾值,如果没有初始化所有将有垃圾值。 我认为,标签联盟只是为了从联盟获得有效的价值而提供一些安慰。 你的问题:你如何或编译器为上面的联合写一个复制构造函数没有额外的信息? sizeof(string)给出4个字节。 基于此,编译器可以比较其他成员的大小和分配最大的分配(在我们的例子中是4字节)。 内部string的长度不重要,因为它将被存储在一个单独的位置。 让string有任何长度。 所有联盟必须知道的是调用带有string参数的string类拷贝构造函数。 无论哪种方式,编译器在正常情况下都需要调用复制构造函数,即使string位于Union内部,也要遵循类似的方法。 所以我想编译器可以这样做,分配4个字节。 然后,如果任何string被分配给s,那么string类将使用它自己的分配器来处理该string的分配和复制。 所以没有内存损坏的机会。

在编译器开发的时候string是不存在的? 所以答案还不清楚。 在这个网站是一个新的合作伙伴,如果有什么不对,请原谅我。

想想看。 编译器如何知道联合中的types是什么?

它不。 工会的根本运作本质上是一个按位投。 包含在工会内的价值观的操作只有在每种types都可以充满垃圾时才是安全的。 std::string不能,因为这会导致内存损坏。 使用boost::variantboost::any

因为在一个联合中有一个非平凡(复制/)构造函数的类是没有意义的。 假设我们有

 union U { string x; vector<int> y; }; U u; // <-- 

如果U是一个结构体,则uxuy将分别初始化为一个空string和空向量。 但是一个工会的成员有着相同的地址。 所以,如果ux被初始化,你将会包含无效的数据,反之亦然。 如果两者都未初始化,则不能使用。 无论如何,把这些数据放在一个联合中是不容易处理的,所以C ++ 98select否认这一点:(§9.5/ 1):

一个具有非平凡构造函数(12.1),非平凡复制构造函数(12.8),非平凡析构函数(12.4)或非平凡复制赋值运算符(13.5.3,12.8)的类的对象不能成为一个工会的成员,也不可能有这样的对象的数组。

在C ++ 0x这个规则已经放宽(§9.5/ 2):

联盟中最多有一个非静态数据成员可能有一个括号或等于初始值设定项 。 [ 注意:如果一个union的非静态数据成员有一个非平凡的默认构造函数(12.1),复制构造函数(12.8),移动构造函数(12.8),复制赋值运算符(12.8),移动赋值运算符(12.8)或析构函数(12.4),联合的相应成员函数必须是用户提供的,否则将被隐式删除(8.4.3)。 – 结束注意 ]

但是为联合创build(正确)con / destructor仍然是不可能的,例如,如果没有额外的信息,你或者编译器如何为联合编写一个拷贝构造函数? 为了确保工会的哪个成员是活跃的,你需要一个加标签的工会 ,你需要手工处理施工和销毁

 struct TU { int type; union { int i; float f; std::string s; } u; TU(const TU& tu) : type(tu.type) { switch (tu.type) { case TU_STRING: new(&u.s)(tu.us); break; case TU_INT: ui = tu.ui; break; case TU_FLOAT: uf = tu.uf; break; } } ~TU() { if (tu.type == TU_STRING) us~string(); } ... }; 

但是,正如@DeadMG所提到的,这已经实现为boost::variantboost::any

在C ++ 98/03中,联合的成员不能有构造函数,析构函数,虚拟成员函数或基类。

所以基本上,你只能使用内置的数据types或POD

请注意,它在C ++ 0x中更改: 无限制的联合

 union { int z; double w; string s; // Illegal in C++98, legal in C++0x. }; 

从C ++规范§9.5.1:

具有非平凡构造函数,非平凡复制构造函数,非平凡析构函数或非平凡复制赋值运算符的类的对象不能是联合的成员。

这个规则的原因是编译器永远不会知道哪个析构函数/构造函数调用,因为它永远不知道这个可能的对象是哪个在union中。

垃圾被引入,如果你

  1. 分配一个string
  2. 然后分配一个int或float
  3. 然后再一个string

stringpipe理其他地方的内存。 这个信息很可能是一些指针。 分配int时,这个指针是垃圾的。 分配一个新的string应该销毁旧的string,这是不可能的。

第二步应该销毁string,但不知道是否有string。

与此同时,他们显然已经find了解决这个问题的办法。