static_cast实际上不是对象类型的类型是未定义的行为吗?
Is it undefined behavior to static_cast down a type that isn't actually the type of the object?
这里是关于四种显式强制转换的堆栈溢出的讨论。 但是我在得票最多的答案中遇到了一个问题。
引用自投票最多的维基答案:
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
吗?
cppreference是用英语编写的,目的是传达良好的理解,它实际上不是规范。但我在这里的措辞没有问题:
此类
static_cast
不进行运行时检查以确保对象的运行时类型实际上是 D,并且只有在通过其他方式保证此前提条件时才能安全使用。
如果你保证前提条件,那就没问题了。如果你不保证前提条件,那么你的代码是不安全的。代码不安全意味着什么?其行为未定义。
实际规范使用以下措辞:
如果 prvalue 的类型 "指向cv1
B
的指针" 指向一个实际上是D
类型的对象的子对象的B
,则生成的指针指向类型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
这是定义良好的行为。
类型为">指向 cv1的指针
B
"的 prvalue,其中B
是类类型,可以转换为类型为"指针"的 prvalue 到cv2D
",其中D
是从B
派生的类(第 10 条),如果从 "指针到D
" 的有效标准转换 对于"指向B
的指针"存在(4.10),CV2与CV1相同或更高的 CV 资格,并且B
既不是D
的虚拟基类,也不是D
的虚拟基类的基类。空指针值 (4.10) 转换为目标类型的空指针值。如果 prvalue 的类型 "指向cv1的指针B
" 点 对于实际上是类型为D
对象的子对象的子对象的B
,生成的指针指向封闭对象 类型D
.否则,行为是未定义的。
(C++14 [expr.static.cast] (§5.9) ¶11,着重号另加)
pa
指向类型为A
的对象,该对象实际上是B
的子对象,因此第二个强制转换很好,结果指向有效的B
。出于同样的原因,你的第三个强制转换是可以的(pb
指向C
的B
子对象)。
cpp偏好所表达的"不安全"部分是关于这里没有安全网的事实:你必须通过自己的方式知道尖头物体的实际动态类型是否与你要求的铸件兼容;如果你弄错了,就没有std::bad_cast
或nullptr
- 你会得到糟糕的旧的未定义行为。
- 在UE4中使用未定义类型'UTextBlock'
- 删除[]具有不同类型的未定义行为?
- 在硬件SIMD矢量指针和相应类型之间进行"interpret_cast"是一种未定义的行为吗
- 错误:未定义对"静脉类型信息::电池访问"的引用
- 在头文件中使用opencv类型来实现未定义的标识符
- 为什么这种类型的双关语不是未定义的行为?
- 在C++中,转换为simd类型是否有未定义的行为
- 是std::memcpy在不同的可复制类型之间的未定义行为
- 交换未定义数据类型中的字节顺序
- 具有未声明/未定义类型的 typedef 结构
- 对静态常量积分类型的未定义引用
- 为什么 std::memcpy(作为类型双关语的替代方案)不会导致未定义的行为?
- 为什么内置类型的对象上的溢出会导致异常/未定义的行为?
- static_cast实际上不是对象类型的类型是未定义的行为吗?
- 共享库中非模板基的模板子类导致未定义的符号类型信息'class'链接错误
- icu::SimpleDate格式使用未定义类型的编译器错误
- 将不相关类型的对象reinterpret_cast空类是未定义的行为吗?
- 类型特征检查 CRTP 派生,在基类中,问题是未定义的类型
- std::is_arithmetic 为通用 lambda 中的 int 类型返回 false:未定义的行为?
- 使用 char16_t 类型作为 char[] 数组,并通过 reinterpret_cast<> 重新转换它。我的代码是否有未定义的行为?