Warning: file_get_contents(/data/phpspider/zhask/data//catemap/3/templates/2.json): failed to open stream: No such file or directory in /data/phpspider/zhask/libs/function.php on line 167

Warning: Invalid argument supplied for foreach() in /data/phpspider/zhask/libs/tag.function.php on line 1116

Notice: Undefined index: in /data/phpspider/zhask/libs/function.php on line 180

Warning: array_chunk() expects parameter 1 to be array, null given in /data/phpspider/zhask/libs/function.php on line 181

Warning: file_get_contents(/data/phpspider/zhask/data//catemap/8/file/3.json): failed to open stream: No such file or directory in /data/phpspider/zhask/libs/function.php on line 167

Warning: Invalid argument supplied for foreach() in /data/phpspider/zhask/libs/tag.function.php on line 1116

Notice: Undefined index: in /data/phpspider/zhask/libs/function.php on line 180

Warning: array_chunk() expects parameter 1 to be array, null given in /data/phpspider/zhask/libs/function.php on line 181
C++ 删除此项并放置新的虚拟派生类 类基{ INTA; 受保护的: 模板 类派生; 公众: base(){} 虚拟~base(){} 虚拟void func(){} 静态基带*maker(); }; 模板 基类::派生类 :公共基地 { 公众: 派生(){} 虚~derived(){} 虚空函数(){ 这个->~derived();//_C++_Templates_Virtual_Placement New - Fatal编程技术网

C++ 删除此项并放置新的虚拟派生类 类基{ INTA; 受保护的: 模板 类派生; 公众: base(){} 虚拟~base(){} 虚拟void func(){} 静态基带*maker(); }; 模板 基类::派生类 :公共基地 { 公众: 派生(){} 虚~derived(){} 虚空函数(){ 这个->~derived();//

C++ 删除此项并放置新的虚拟派生类 类基{ INTA; 受保护的: 模板 类派生; 公众: base(){} 虚拟~base(){} 虚拟void func(){} 静态基带*maker(); }; 模板 基类::派生类 :公共基地 { 公众: 派生(){} 虚~derived(){} 虚空函数(){ 这个->~derived();//,c++,templates,virtual,placement-new,C++,Templates,Virtual,Placement New,我认为这显然不好 作为前言,调用析构函数确实可以结束对象的生命周期,但您只允许(并且必须)在其位置构造相同类型的新对象: class base { int a; protected: template<class T> class derived; public: base() {} virtual ~base() {} virtual void func() {} static base* maker(); }; templ

我认为这显然不好

作为前言,调用析构函数确实可以结束对象的生命周期,但您只允许(并且必须)在其位置构造相同类型的新对象:

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>()?
}
请记住,在作用域的末尾将调用析构函数

但在你的例子中,你正在构建一个完全不同的对象:

::new(&x)Bar;//Bar=DerivedA
显然,如果
sizeof(Bar)
超过
sizeof(Foo)
这是不正常的

(如果您可以对对象大小以及对齐保证做出额外保证,我们可以进一步考虑。)

更新:即使你在想,好吧,这些类型都是从同一个基派生的,所以调用析构函数会带来虚拟的快乐,我仍然很确定这是一种违反。在这个静态设置中,编译器可能会静态地解析虚拟调用,因此如果你更改
&x
指向的对象


更新2:关于同一问题的另一个想法是:
*&x
的静态类型是已知的,我认为您必须尊重这一点。换句话说,编译器没有理由考虑局部变量的静态类型发生变化的可能性。

我很确定这是无效的代码,原因有几个:

  • 如果插入的类型大小不同,则会发生不好的事情(我不太确定,但我认为标准没有对类型的大小做出太多承诺,因此从理论角度来看,可能很难证明它们具有相同的大小(通过实践,它们很可能会)
  • 如果变量的类型是静态已知的(可能是因为它是在堆栈上构造的,但理论上它可以做同样的事情,如果它可以看到allocaiton并证明指针不能被修改),编译器可以随意静态地重新解决虚方法调用(例如析构函数)使用它们显然会破坏代码
  • 即使变量的类型不是静态已知的,我很确定编译器可以假设它的类型在其生命周期内不会改变(指针在函数内部不会改变,所以它应该能够假设指向的类型也不会改变)。因此,虽然它不能静态解析方法,但它可能会重用以前调用虚拟方法(例如,更改类型的方法)时使用的vmt指针
  • 编辑:现在我想这不会破坏严格的别名规则,因为在放置新的之后,它指向一个不兼容的类型?虽然它不会在函数中再次被明确访问,但我认为不能保证编译器不会插入访问(尽管可能性很小)。无论如何,这意味着编译器可以假定这种操作不会发生

    <强>编辑:< /强>当我查看新的C++标准时,我发现[Basy.Leave](Pr.3.85)给出了与未定义行为的例子基本相同的东西(它实际上并没有破坏对象,但我看不出这能使事情变得更好):

    #包括
    结构{
    虚空f();
    空变();
    虚拟~B();
    };
    结构D1:B{void f();};
    结构D2:B{void f();};
    void B::mutate(){
    new(this)D2;//重用存储结束*this的生命周期
    f();//未定义的行为
    …=this;//好的,这指向有效内存
    }
    void g(){
    void*p=std::malloc(sizeof(D1)+sizeof(D2));
    B*pb=新的(p)D1;
    pb->mutate();
    &pb;//确定:pb指向有效内存
    void*q=pb;//确定:pb指向有效内存
    pb->f();//未定义的行为,*pb的寿命已结束
    }
    

    这应该证明这是不允许的。

    不管它是否正确。头脑正常的人都不会这样做。即使它确实有效,也将是维护的噩梦,因此在实际代码中永远不会发生。离题:为什么要转换到怪物指针?Placement new需要一个空指针。也许您的案例是一个候选者对于动态强制转换为无效!
    void*p=dynamic\u cast(this);new(p)T;
    我很确定,如果用于堆栈上声明的变量,这将严重损坏。@KerrekSB我是否感觉到您试图从本周早些时候的一个问题中学到的东西中获得乐趣?:)@MatteoItalia:不,这完全可以(见我的答案).w00te:有一分钟,我以为我们可能在这里发现了一些东西,但现在我确信这毫无意义:-(在我的OP中,注意到它们保证大小相同。在您的更新中:这在符合要求的编译器中是允许的吗?我是违反规范还是编译器?(假设它不工作。我还没有实际测试。)@MooingDuck:如果你的例子是字面意思,那么你不需要任何虚拟层次结构或任何析构函数,所以你可能很好。我的答案是针对一个更一般的情况,我猜。我重新阅读了Matteo Italia,意识到他在我的SSCCE中发现了潜在的错误,而这些错误不在我的真实代码中,所以我更新了这个问题澄清不能在堆栈上创建
    派生的
    。但我认为这不会影响您的答案。在实际代码中,
    包含一个缓冲区,
    派生的
    正在将
    t
    放入其中,并且虚拟成员对该
    t
    进行操作。需要虚拟层次结构。@moing:如果您有不管怎样,为什么在基类中有一个缓冲区,而不是在de中有一个正确类型的值
    {
      Foo x;
      x.~Foo();
      ::new (&x) Foo;
    }  // x.~Foo() must be a valid call here!
    
    ::new (&x) Bar;   // Bar = DerivedA<int>
    
    #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
    }