class base {
int a;
protected:
template<class T>
class derived;
public:
base() {}
virtual ~base() {}
virtual void func() {}
static base* maker();
};
template <class T>
class base::derived
: public base
{
public:
derived() {}
virtual ~derived() {}
virtual void func() {
this->~derived(); //<--is this legal?
new (this) derived<int>(); //<--is this legal?
}
};
base* base::maker() {
return new derived<double>();
}
int main() {
base* p = base::maker(); //p is derivedA<double>
p->func(); //p is now derivedA<int>
delete p; //is the compiler allowed to call ~derived<double>()?
}
这是一个简短的,自包含的,正确的(可编译的),我的代码示例(基本上是为了我自己的成长而重新发明any_iterator
)。
这个问题简化为:当共享基础上没有任何其他成员时,是否会使用从同一个基础派生的不同类型来销毁this
和重构this
的未定义行为?具体来说,编译器允许调用跟踪静态类型,还是技术上不符合?
[编辑]有人指出,如果在堆栈上创建derivedA
,编译器可能会调用不正确的析构函数。 (1)我在标准中找不到任何允许编译器这样做的东西。 (2)我的问题除了我的意图之外,所以我更改了代码以显示derived
不能放在堆栈上。 base
仍然可以在堆栈中。
答案 0 :(得分:3)
我认为这显然不行。
作为前言,对象的生命周期确实可以通过调用析构函数来结束,但只允许(并且必须)在其位置构造一个相同类型的新对象: / p>
请记住,在范围结束时,将调用析构函数! 但在你的情况下,你正在构建一个完全不同的对象: 显然,如果 (也许如果您可以对对象大小以及对齐保证做出额外保证,我们可以进一步考虑这一点。) 更新:即使您正在思考,好吧,所以这些类型来自同一个基础,因此调用析构函数会带来虚拟的快乐,我仍然非常确定这是违规行为。在这种静态设置中,编译器可能会静态地解析虚拟调用,因此如果更改 更新2:关于同一事项的另一个想法:{
Foo x;
x.~Foo();
::new (&x) Foo;
} // x.~Foo() must be a valid call here!
::new (&x) Bar; // Bar = DerivedA<int>
sizeof(Bar)
超过sizeof(Foo)
,这可能不行。&x
指向的事物的动态类型,则会破坏编译器的假设。*&x
的静态类型是已知的,我认为你必须尊重这一点。换句话说,编译器没有理由考虑局部变量的静态类型发生变化的可能性。
答案 1 :(得分:1)
由于以下几个原因,我很确定这是无效的代码:
编辑:现在我认为它不会破坏严格的别名规则,因为在放置后新的这指向不兼容的类型?授予它不再在函数中明确访问,但我不认为可以保证编译器不会插入访问(尽管不太可能)。无论如何,这意味着编译器可以假设不会发生这种行为。
编辑:在查看新的C ++标准时,我发现[basic.life](§3.8.5)给出了与未定义行为的示例基本相同的东西(它没有'实际上是摧毁了这个物体,但是我不知道这会怎样才能让事情变得更好呢:
#include<cstdlib>
structB{
virtual void f();
void mutate();
virtual ~B();
};
struct D1:B { void f(); };
struct D2:B { void f(); };
void B::mutate(){
new(this)D2; //reuses storage—ends the lifetime of *this
f(); //undefined behavior
...=this; //OK, this points to valid memory
}
void g(){
void* p = std::malloc(sizeof(D1) + sizeof(D2));
B* pb = new(p)D1;
pb->mutate();
&pb; //OK: pb points to valid memory
void* q = pb; //OK: pb points to valid memory
pb->f(); //undefined behavior, lifetime of *pb hasended
}
这应该证明这是不允许的。