为什么编译器不允许std :: string在union中?

bjs*_*123 45 c++

我想在Union里面使用字符串.如果我写如下

union U
{
   int i;
   float f;
   string s;
};
Run Code Online (Sandbox Code Playgroud)

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

我读了一些其他帖子,了解解决这个问题的其他方法.但我想知道为什么编译器首先不允许这样做?

编辑:@KennyTM:在任何联合中,如果成员被初始化,其他人将具有垃圾值,如果没有初始化,则所有将具有垃圾值.我认为,标记联合只是为从Union访问有效值提供了一些安慰.您的问题:您或编译器如何在没有额外信息的情况下为联合编写复制构造函数?sizeof(string)给出4个字节.基于此,编译器可以比较其他成员大小并分配最大分配(在我们的示例中为4字节).内部字符串长度无关紧要,因为它将存储在单独的位置.让字符串为任意长度.Union必须知道的是使用字符串参数调用字符串类复制构造函数.无论哪种方式编译器发现在正常情况下都必须调用复制构造函数,即使字符串在Union中,也要遵循类似的方法.所以我认为编译器可以这样做,分配4个字节.然后,如果为s分配了任何字符串,则字符串类将使用其自己的分配器来处理该字符串的分配和复制.所以也没有内存损坏的可能性.

编译器中Union开发时不存在字符串吗?所以我的答案还不清楚.我是这个网站的新工作人员,如果有什么不对,请原谅.

ken*_*ytm 58

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

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

U u;  // <--
Run Code Online (Sandbox Code Playgroud)

如果U是一个结构,u.x并且u.y将分别初始化为空字符串和空向量.但是工会成员共享同一个地址.因此,如果u.x初始化,u.y将包含无效数据,反之亦然.如果它们都未初始化,则无法使用它们.在任何情况下,在联合中使用这些数据都不容易处理,因此C++ 98选择否认:(§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).- 结束说明 ]

但是仍然无法为联合创建(正确)con /析构函数,例如,如果没有额外信息,您或编译器如何为上面的联合编写复制构造函数?要确保联合的哪个成员处于活动状态,您需要一个标记的联合,并且需要手动处理构造和破坏,例如

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.u.s); break;
       case TU_INT:    u.i = tu.u.i;      break;
       case TU_FLOAT:  u.f = tu.u.f;      break;
     }
   }
   ~TU() {
     if (tu.type == TU_STRING)
       u.s.~string();
   }
   ...
};
Run Code Online (Sandbox Code Playgroud)

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


Pup*_*ppy 26

想一想.编译器如何知道联合中的类型?

它没有.联盟的基本操作基本上是一个按位演员.联合中包含的值的操作只有在每种类型基本上都可以填充垃圾时才是安全的.std::string不能,因为这会导致内存损坏.使用boost::variantboost::any.

  • @bjskishore:sizeof(string)不可移植.此外,编译器无法找到要调用的构造函数/ etc,因为它不知道union中的类型.所有函数调用必须在编译时或虚拟时已知,并且union既不是多态的也不是编译时常量.所以编译器永远不知道该怎么做.另外,大小真的,真的无所谓 - 问题在于它是一个指针.如果你在联合中存储一个浮点然后访问一个字符串,你将有一个二进制浮点作为指针处理,这几乎肯定会指向无效的内存并使程序崩溃. (3认同)
  • @Puppy请修正答案:OP要求的代码在C ++ 11及更高版本中有效。 (2认同)

Kea*_*eks 14

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

基本上,您只能使用内置数据类型或POD

请注意,它在C++ 0x中发生了变化:Unrestricted union

union {
    int z;
    double w;
    string s;  // Illegal in C++98, legal in C++0x.
};
Run Code Online (Sandbox Code Playgroud)


Fir*_*his 7

从C++规范§9.5.1开始:

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

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