以下是四种明确演员的discussion on stackoverflow。但我在最投票的答案中遇到了一个问题。
引自投票最多的维基回答:
static_cast
也可以通过继承层次结构进行转换。向上(向基类)进行向上渲染时是不必要的,但向下向下时,只要它不通过virtual
继承进行强制转换就可以使用它。但是,它没有进行检查,并且将层次结构中的static_cast
定义为实际上不是对象类型的类型是未定义的行为。
但在cppref中,我读的内容不那么严重:
static_cast < new_type > ( expression )
如果new_type是指向某个类D的指针或引用,并且表达式的类型是指向其非虚拟基数B的指针或引用,则
static_cast
执行向下转换。如果B是D的模糊,不可访问或虚拟基础(或虚拟基础的基础),则此向下转换是不正确的。这样static_cast
不进行运行时检查以确保对象的运行时类型实际上是D,如果通过其他方式保证此前提条件,则只能安全使用。
所以在cppref中它并没有说未定义的行为,而是不安全。
所以当我做类似的事情时:
class A{virtual foo(){}};
class B:public A{};
class C:public B{};
int main()
{
C*pc=new C;
A*pa=static_cast<A*>(pc);//Ok,upcast.
B*pb=static_cast<B*>(pa);//downcast,**is it undefined or just not safe?**
C* pc1=static_cast<C*>(pb);//downcast back to C;
}
还有一个问题,如果它不是UB,那么UB是否需要取消引用pb
?
答案 0 :(得分:4)
cppreference用英文写成,旨在传达良好的理解,实际上并不是规范。但我的措辞没有问题:
这样的
static_cast
不进行运行时检查以确保对象的运行时类型实际上是D,并且只有在通过其他方式保证此前提条件时才可以安全使用。
如果你保证前提条件,那很好。如果您不保证前提条件,那么您的代码就不安全了。代码不安全是什么意思?它的行为没有定义。
实际规范使用this wording:
如果类型“指向 cv 1
B
的指针”的prvalue指向B
,它实际上是一个对象的子对象键入D
,结果指针指向类型为D
的封闭对象。否则,行为未定义。
无论哪种方式,在您的示例中,您的所有演员表都是有效的。
B*pb=static_cast<B*>(pa);//downcast,**is it undefined or just not safe?**
你错过了这个条件。垂头丧气本身并不是不确定的行为。如果实际上没有派生类型的对象,那么它只是未定义的行为。在这种情况下,pa
指向C
,这是B
,因此转换为B
是安全的。
然而,这不是:
struct B { };
struct D1 : B { };
struct D2 : B { };
B* p = new D1;
static_cast<D2*>(p); // undefined behavior, no D2 object here
答案 1 :(得分:2)
这是明确定义的行为。
类型为“指向 cv1
B
的指针的prvalue”,其中B
是类类型,可以转换为类型为“指针”的prvalue 到 cv2D
,“其中D
是B
派生的类(第10条),如果是从”D
指针的有效标准转换}” “指向B
”的指针存在(4.10), cv2 与 cv1 具有相同的cv资格,或更高的cv资格,并且B
既不是D
的虚基类,也不是D
的虚基类的基类。空指针值(4.10) 转换为目标类型的空指针值。 如果类型为“指向 cv1B
的指针”的prvalue指向 到B
实际上是D
类型对象的子对象,结果指针指向封闭对象 类型D
。否则,行为未定义。
(C ++ 14 [expr.static.cast](§5.9)¶11,重点补充)
pa
指向A
类型的对象,它实际上是B
的子对象,因此第二个强制转换很好,结果指向有效的B
。由于同样的原因(pb
指向B
的{{1}}子对象),您的第三次演员阵容就行了。
cppreference表示的“不安全”位是关于这里没有安全网的事实:你必须通过自己的方式知道指向对象的实际动态类型是否与你要求的演员兼容;如果你弄错了,那就没有C
或std::bad_cast
- 你会得到糟糕的旧的未定义行为。