【发布时间】:2011-01-26 17:19:22
【问题描述】:
我知道 C++ 不支持容器元素的协方差,就像在 Java 或 C# 中一样。所以下面的代码可能是未定义的行为:
#include <vector>
struct A {};
struct B : A {};
std::vector<B*> test;
std::vector<A*>* foo = reinterpret_cast<std::vector<A*>*>(&test);
毫不奇怪,当我建议这是another question 的解决方案时,我收到了反对票。
但是 C++ 标准的哪一部分确切地告诉我这将导致未定义的行为?保证std::vector<A*> 和std::vector<B*> 都将它们的指针存储在一个连续的内存块中。还保证sizeof(A*) == sizeof(B*)。最后,A* a = new B 是完全合法的。
那么,我想到了标准中的哪些不良情绪(风格除外)?
【问题讨论】:
-
在此之后使用 reinterpret_cast() 没有定义任何内容。它可能有效,但您的条件列表非常短。我会添加另外几个前提条件。大小(A)==大小(B); A 或 B 都不能包含任何类型的虚函数。 A 和 B 以及放置在数组中的任何后代都不能使用多重继承。
-
非 C++ 特定的答案是它不是类型安全的。如果将
A添加到 foo 中,则测试处于无效状态,因为它保证所有元素都是B类型。 C# 也不支持这个。 C# 仅支持以安全方式使用的泛型参数(仅输入或输出)并且仅在接口和委托上使用。 Java 支持它,因为它添加了运行时检查并在内部处理对象基类。 -
@CodeInChaos:是什么让你这么认为?整个想法是有缺陷的,会破坏类型系统。存在一个简单的测试here。泛型允许函数参数中的协变和逆变,这取决于您想要做什么,例如在
void append( Vector<? super Derived> v ) { v.add( new Derived() ); }或void extract( Vector<? extends Base> v ) { Base b = v.get(0); }中,但它不允许转换引用。 -
函数中允许使用的原因是泛型只是编译时类型检查,并且泛型类型从二进制文件中删除。当您在函数中使用协变/逆变参数时,编译器可以检查函数内部的操作是否不会破坏接口的要求。在调用方,它可以检查相同的要求,然后将始终作为非泛型
Vector(包含Object)的引用传递。另一方面,如果允许转换,那么您将能够将Base对象添加到Derived的容器中。
标签: c++ covariance