引用对象的动态类型何时可以更改
When may the dynamic type of a referred to object change?
让我们从一个例子开始:
#include <cstdio>
struct Base { virtual ~Base() {} virtual void foo() = 0; };
struct P: Base { virtual void foo() override { std::printf("Hello, World!"); } };
struct N: Base { virtual void foo() override {} };
void magic(Base& b);
// Example implementation that changes the dynamic type
// {
// void* s = dynamic_cast<void*>(&b);
// b.~B();
// new (s) N();
// }
int main() {
std::aligned_storage<sizeof(P), alignof(P)> storage;
void* s = static_cast<void*>(storage);
new (s) P();
Base& b = *static_cast<Base*>(s);
magic(b);
b.foo();
}
根据标准,b.foo()
应该打印什么?
个人观点:它是未定义的,因为b
在我们销毁了magic
中的实例后变得过时了。在这种情况下,用static_cast<B*>(s)->foo()
代替b.foo()
是否合法
因此,现在我们有了一个可能(或不)合法的例子,对于我们所有的标准化者来说,手头更普遍的问题是是否允许更改对象的动态类型。我们已经知道C++编译器可能会重用存储(幸运的是),所以这有点棘手。
这个问题可能看起来是理论性的,但它对编译器有直接的应用:编译器可能会在上面的程序中使b.foo()
到b.P::foo()
失效吗?
因此,我正在寻找:
- 关于我自己的小程序,这是一个明确的答案(我无法想出一个)
- 一个改变对象动态类型的合法方法的可能例子(一个就足够了)
根据标准§8.5.3.2,初始化后引用不能绑定到另一个对象。由于放置new
会创建一个新对象,因此您违反了该规则,并获得了未定义的行为。
对象的动态类型无法更改。即使在您的示例中,您也不是在更改对象的类型,而是在与旧对象相同的位置创建一个不同的新对象。如果你仔细想想,更改对象的动态类型意味着调整对象的大小以容纳额外的数据成员,并更改VMT(然后会移动其他对象并损坏指针…),这在语言规则范围内是无法做到的。
这是未定义的行为。您的magic
示例违反了引用的语义。
此外,dynamic_cast
用于向下铸造。铸造到void*
的是static_cast
。
明确回答您的问题:
- 如果编译器能够证明运行时类型,它可以"破坏"它喜欢的任何函数调用
- 如果引用比它引用的对象寿命长,那么它就是UB
-
您不能更改对象的动态类型,您能做的最接近的事情就是重新分配指针。
Base * ptr; P p; N n; ptr = &p; ptr -> foo (); ptr = &n; ptr -> foo ();
但是p
和n
是固定类型的,直到它们超出范围(或者,如果在堆上分配,当它们是delete
d时)。
相关文章:
- ";结果类型必须是可从输入范围的值类型""构造的;创建std::vector时
- 错误 C2679:二进制"<<":未找到采用类型 'std::string_view' 的右侧操作数的运算符(或者没有可接受的转换)
- 类作用域的类型别名"using":[何时]方法中的用法可以先于类型别名?
- C2678 二进制 '==':未找到采用 'Card' 类型左操作数的运算符(或者没有可接受的转换)
- 如果可推导类型上有替换,可变参数模板类型推导会使编译器崩溃
- 为什么协程的返回类型必须是可移动构造的?
- 可视化 如何在C++中将字符数组转换为 FILE 类型
- 了解类型是否可调用
- 将"模板<类型名 T>zero()"扩展/专用为可调用的"T"
- 检查模板中 nullptr 的函数指针,了解任何类型的可调用对象
- 当前不会命中断点。没有调试器目标代码类型的可执行代码与此文件关联
- Eclipse 可执行架构类型
- 当类型适当的构造函数可用时,为什么一个编译器尝试使用已删除的副本构造函数
- 何时包含内置类型和运算符的标头?
- reinterpret_cast,只读访问,简单的可复制类型,会出什么问题?
- 二进制 '==':未找到采用 'Enemy' 类型左侧操作数的运算符(或者没有可接受的转换)
- 隐式可转换参数,但属于引用类型
- 从非类型模板参数声明 constexpr 数组的可移植方法
- 如何检测磁贴何时可遍历
- 如何为程序生成生成可类型的种子?