工会作为基类

Alo*_*ave 36 c++ inheritance unions language-lawyer

该标准定义了Unions不能用作Base类,但是有没有具体的推理呢?据我所知,Unions可以有构造函数,析构函数,成员变量和操作这些varibales的方法.简而言之,Union可以封装可以通过成员函数访问的数据类型和状态.因此,它在大多数常见术语中有资格成为一个类,如果它可以作为一个类,那么为什么它被限制作为一个基类呢?

编辑:虽然答案试图解释推理我仍然不明白联盟作为派生类是如何比联盟只是一个类更糟糕.因此,为了获得更具体的答案和推理,我将推动这一点获得赏金.对已经发布的答案没有违法行为,谢谢!

Ytt*_*ill 14

Tony Park给出了一个非常接近事实的答案.C++委员会基本上认为让工会成为C++的一个重要组成部分是不值得的,类似于将数组作为遗留物来处理,我们不得不从C继承,但并不真正想要.

工会有问题:如果我们在工会中允许非POD类型,它们如何构建?它当然可以完成,但不一定安全,任何考虑都需要委员会资源.最后的结果将不太令人满意,因为在理智的语言中真正需要的是受歧视的工会,而且简单的C工会永远不会被提升到与C兼容的有区别的工会(我无论如何都可以想象).

详细说明技术问题:既然你可以在一个结构中包含POD组件只有一个联合而不会丢失任何东西,那么允许联盟作为基础是没有优势的.对于仅使用POD的联合组件,显式构造函数只需分配其中一个组件,也不会使用bitblit(memcpy)来编译生成的复制构造函数(或赋值).

然而,这样的联合没有足够的麻烦,除了保留它们,因此现有的C代码可以被认为是有效的C++.这些仅限POD的联合在C++中被破坏,因为它们无法保留它们在C中拥有的重要不变量:任何数据类型都可以用作组件类型.

为了使联合有用,我们必须允许可构造类型作为成员.这很重要,因为仅仅在构造函数体中分配组件(联合本身或任何封闭结构)是不可接受的:例如,您不能将字符串分配给未初始化的字符串组件.

接下来必须发明一些用于使用mem-initialisers初始化union组件的规则,例如:

union X { string a; string b; X(string q) : a(q) {} };
Run Code Online (Sandbox Code Playgroud)

但现在的问题是:规则是什么?通常规则是你必须初始化一个类的每个成员和基类,如果你没有显式地这样做,默认构造函数用于余数,如果一个未显式初始化的类型没有默认构造函数,它是一个错误[例外:复制构造函数,默认为成员复制构造函数].

显然,此规则不适用于联合:规则必须是:如果联合至少有一个非POD成员,则必须在构造函数中明确初始化一个成员.在这种情况下,不会生成默认构造函数,复制构造函数,赋值运算符或析构函数,如果实际使用了这些成员中的任何一个,则必须显式提供它们.

所以现在问题变成了:你会怎么写一个复制构造函数?当然,如果你按照X-Windows事件工会的设计方式来设计工会,那么当然很有可能做得很好:在每个组件中都有判别标记,但是你必须使用新的操作符来完成它,你将不得不违反我上面写的规则,乍一看似乎是正确的!

那么默认构造函数呢?如果您没有其中一个,则无法声明未初始化的变量.

在其他情况下,您可以在外部确定组件并使用placement new来管理外部联合,但这不是复制构造函数.事实是,如果你有N个组件,你需要N个构造函数,而C++有一个破碎的想法,构造函数使用类名,这使你缺少名称并迫使你使用幻像类型来允许重载选择正确的构造函数..并且您不能为复制构造函数执行此操作,因为它的签名是固定的.

好的,还有替代品吗?可能,是的,但是他们并不是那么容易想到,并且更难说服100多人在一个充满其他问题的三天会议中考虑值得思考.

遗憾的是,委员会没有实施上述规则:工会对于任意数据的排序是强制性的,组件的外部管理实际上并不难以做到,并且当代码由合适的算法生成时,琐碎且完全安全,换句话说,该规则是强制性的如果你想使用C++作为编译器目标语言,仍然生成可读的可移植代码.具有可构造成员的此类联合有许多用途,但最重要的是表示包含嵌套块的函数的堆栈框架:每个块在结构中都有本地数据,每个结构都是一个联合组件,不需要任何构造函数或者这样,编译器将只使用placement new.联合提供了对齐和大小,以及免费的组件访问.[并没有其他符合标准的方法来获得正确的对齐方式!]

因此,你的问题的答案是:你问的是错误的问题.POD唯一的工会作为基础是没有优势的,它们当然不能是派生类,因为它们不会是POD.为了使它们有用,需要一些时间来理解为什么应该遵循C++中其他地方使用的原则:除非您尝试使用它们,否则缺少的位不是错误.


sha*_*oth 9

Union是一种可以用作其任何一个成员的类型,具体取决于已设置的成员 - 只有该成员可以在以后读取.

从派生类型派生时,派生类型继承基类型 - 派生类型可以在基类型的任何位置使用.如果您可以从联合派生,则可以使用派生类(不是隐式地,但通过命名成员显式地),无论何处可以使用任何联合成员,但在这些成员中,只有一个成员可以合法访问.问题是已设置成员的数据未存储在联合中.

为了避免这种微妙但危险的矛盾,实际上颠覆了一种来自联盟的类型系统是不允许的.

  • 我认为我不理解这个论点.当然,只有一个联盟成员可以访问,但是这如何使派生类比基类更"糟糕"?因为它本身也是真的(不是作为基类),我不知道这是如何阻止派生类完全替代基类的.LSP中没有任何内容表明派生类(或联合)需要替换其*成员之一*. (5认同)

use*_*421 7

Bjarne Stroustrup在"Annotated C++参考手册"中说"似乎没有理由".