是否存在将实际Base下转换为Derived的情况
Are there cases where downcasting an actual Base to a Derived would be defined?
在一般情况下,从(动态)Base
向下投射到其中一个派生类Derived
是(当之无愧的)未定义行为
显而易见的UB
class Base
{
public:
virtual void foo()
{ /* does something */ }
int a;
}
class Derived : public Base
{
public:
virtual void foo()
{ /* does something different */ }
double b;
}
Base obj;
Derived derObj = *static_cast<Derived *>(&obj); // <- here come the demons
在当前编译器的实现方法中,显然至少存在Vtable和b中的值不一致的问题,其中包含垃圾值。因此,该标准没有定义在这些条件下的下变频行为是有道理的。
不那么明显的天真案例
然而,我很想知道在特定情况下是否对此规则做出了一些让步例如:
class Base
{
public:
void foo()
{ /* does something */ }
int a = 1;
double b = 2.;
}
class DerivedForInt : public Base
{
int getVal()
{ return a }
}
Base obj;
DerivedForInt derObj = *static_cast<DerivedForInt *>(&obj); // <- still an UB ?
在这里,我们可以很容易地想象编译器做正确的事情但从标准的角度来看,它还没有定义吗
编辑:static_cast是出于说明目的的随机选择,如果与其他演员一起工作,也很有趣!
好吧,我可能会把这个答案切成碎片。。。
显然,正如其他答案所说,这是标准中未定义的行为。但是,如果Base
类具有标准布局,并且DerivedForInt
类没有添加新的数据成员,则它将具有相同的(标准)布局。
在这些条件下,你的演员阵容应该不会造成任何麻烦,即使是UB。根据其中一个消息来源,进行至少是安全的
DerivedForInt *derived = reinterpret_cast<DerivedForInt*>(&base.a);
来源:
什么是聚合和POD,它们是如何/为什么特别的?
POD和C++11中的继承。结构==的地址是第一个成员的地址吗?
来自第二个链接:
以下是标准第9节【类别】中的定义:
标准布局类是指:
- 不具有非标准布局类(或此类类型的数组)或引用类型的非静态数据成员
- 没有虚拟函数(10.3),也没有虚拟基类(10.1)
- 对所有非静态数据成员具有相同的访问控制(条款11)
- 没有非标准布局基类
- 在最派生的类中没有非静态数据成员,并且最多有一个基类具有非静态数据会员,或者没有基类具有非静止数据会员,以及
- 没有与第一个非静态数据成员类型相同的基类
然后你想要的财产得到保证(第9.2节[class.mem]):
一个指向标准布局结构对象的指针,使用interpret_cast进行适当转换,指向其初始成员(或者,如果该成员是位字段,则指向其所在的单元),反之亦然。
这实际上比旧的要求要好,因为添加非平凡的构造函数和/或析构函数不会丢失重新解释_ ast的能力。
n3376 5.2.9/11
由于"pointer to cv1 B"类型的prvalue,其中B是类类型,可以转换为"pointer到cv2D,"其中D是从B派生的类(第10条),如果从"指针到D"的有效标准转换对于"指向B的指针"的存在(4.10),cv2与cv1具有相同的cv资格,或者比cv1具有更大的cv资质,并且B既不是D的虚拟基类,也不是D的虚基类的基类。空指针值(4.10)被转换为目标类型的空指针值。
如果类型为"pointer to cv1 B"的prvalue指向一个B,该B实际上是类型为D的对象的子对象,则得到的指针指向封闭对象类型为D。否则,强制转换的结果是未定义的
&obj
不指向DerivedForInt
,因此它是UB。
这仍然是未定义的行为,我认为应该是。
为什么未定义
正如@ForEverR在他的回答中提供的:
n3376 5.2.9/11
类型为"pointer to cv1 B"的prvalue,其中B是类类型,可以转换为类型为"pointer to cv2 D"的prvalue,其中D是从B 派生的类(第10条)
如果类型"pointer to cv1 B"的prvalue指向实际上是D类型对象的子对象的B,则结果指针指向D类型的封闭对象。否则,强制转换的结果是未定义的。
为什么应该未定义
它只适用于POD类型,因为在你的基础上添加一个虚拟函数就足以伤害我所知道的所有编译器。此外,类型之间的差异可能是概念上的,而不仅仅是数据布局上的差异。类型安全与防止数据表示问题一样,也与提供强抽象有关。
如果您想要这样的东西,最好将其作为一个普通函数提供,或者在派生类中添加一个构造函数,该构造函数接受基类的实例。
- 防止主数据类型C++的隐式转换
- 将sharet_ptr<Derived>转换为shared_ptr<Base>
- 如何将unique_ptr<derived>*转换为<base>unique_ptr*?
- 为什么在此示例中从unique_ptr自动向上转换<derived>到unique_ptr<base>失败?
- 候选函数不可行:没有从 std::vector<derived> 到 std::vector <base>的已知转换
- 派生到矩阵库转换背后的故事<Derived>
- 如何将标准::make_unique<derived>() 转换为标准::unique_ptr<base>
- 将 std::shared_ptr<Derived> 转换为 const <Base>shared_ptr&
- 特征::<Derived> <Derived> 当列数固定时,无法将块转换为 Ref
- 从 Foo<Derived> 到 Foo <Base>的转换
- 为什么不允许从 VirtualBase::* 转换为 Derived::*?
- 无法将"derived"强制转换为其私有基类"base"
- 是否存在将实际Base下转换为Derived的情况
- 无法将"member pointer to derived class"转换为"member pointer to base class"
- 如果 Derived 没有向 Base 添加新成员(并且是 POD),则可以安全地完成哪种指针强制转换和取消引用
- <derived> 从函数返回时如何从shared_ptr转换为shared_ptr<base>?
- C++将 std::vector<Derived>* 转换为 std::vector<Base> ...?
- 将 std:vector<reference_wrapper<Base>> 转换为 std:vector<reference_wrapper<Derived>
- 在基ctor初始化程序中将其强制转换为“Derived*”合法吗
- 从Base*容器向下强制转换为Derived*容器,无需显式转换