new (ptr) T() == static_cast<T*>(ptr)?

余国良*_*余国良 6 c++ standards

我想实现像 rust 的 dyn 特性这样的东西(我知道这不适用于多重继承)

\n
\n
template<template<typename> typename Trait>\nclass Dyn\n{\n    struct _SizeCaler:Trait<void>{ void* _p;};\n    char _buffer[sizeof(_SizeCaler)];\npublic:\n    template<typename T>\n    Dyn(T* value){\n        static_assert(std::is_base_of_v<Trait<void>,Trait<T>>\n            ,"error Trait T,is not derive from trait<void>"\n        );\n\n        static_assert(sizeof(_buffer) >= sizeof(Trait<T>)\n            ,"different vtable imple cause insufficient cache"\n        );\n\n        new (_buffer)Trait<T>{value}; \n    }\n    Trait<void>* operator->(){\n        return static_cast<Trait<void>*>(reinterpret_cast<void*>(_buffer));\n    }\n};\n\ntemplate<template<typename> typename Trait,typename T>\nstruct DynBase:Trait<void>\n{\nprotected:\n    T* self;\npublic:\n    DynBase(T* value):self(value){}\n};\n\nstruct Point{\n    double x,y;\n};\n\nstruct Rect{\n    Point m_leftDown,m_rightUp;\n    Rect(Point p1,Point p2){\n        m_leftDown = Point{std::min(p1.x,p2.x),std::min(p1.y,p2.y)}; \n        m_rightUp = Point{std::max(p1.x,p2.x),std::max(p1.y,p2.y)}; \n    }\n};\n\ntemplate<typename = void>\nstruct Shape;\n\ntemplate<>\nstruct Shape<void>\n{\n    virtual double area() = 0;\n};\n\ntemplate<>\nstruct Shape<Rect> final\n    :DynBase<Shape,Rect>\n{\n    using DynBase<Shape,Rect>::DynBase;\n    double area() override{\n        return (self->m_rightUp.x - self->m_leftDown.x )\n            * (self->m_rightUp.y - self->m_leftDown.y);\n    }\n};\n\nvoid printShape(Dyn<Shape> ptr)\n{\n    std::cout << ptr->area();\n}\n\nint main()\n{\n    Rect r{{1,2},{3,4}};\n    printShape(&r);\n}\n\n
Run Code Online (Sandbox Code Playgroud)\n
\n

但我发现c++标准可能无法推导出 \xe2\x80\x9cnew (ptr) T() == static_cast<T*>(ptr)\xe2\x80\x9c?\n所以相反, \xe2 \x80\x9cstatic_cast<T*>(ptr) == new (ptr) T()\xe2\x80\x9d 无法证明

\n
Trait<void>* operator->(){ return static_cast<Trait<void>*>(reinterpret_cast<void*>(_buffer)); }\n
Run Code Online (Sandbox Code Playgroud)\n

其他尝试

\n

抽象类不能是联合成员(为什么?),并且我不能使用放置 new 在编译时计算偏移量,因为 Trait 是一个抽象类。

\n

那么标准是否规定了 static_cast<T*>(ptr) == new (ptr) T() 的有效性呢?

\n

Ben*_*igt 1

表达式new (ptr) T()and static_cast<T*>(ptr),其中ptr有 type void*,将返回相同的地址ptr(只要T是标量类型——动​​态分配时允许数组类型有开销)

然而,语义却截然不同。

  • 在 中,在指定地址创建new (ptr) T()一个类型的新对象。T任何先前的价值都会丢失。

  • 在 中static_cast<T*>(ptr),指定地址处必须已经有一个类型的对象T,否则您将自己设置为违反严格的别名规则。


如果ptr具有 以外的类型void*,则需要T通过继承或简单调整(例如更改符号或添加constvolatile)来关联它,并且由于多重继承,该地址可能会应用偏移量。但这在这个问题的代码中永远不会发生。

  • @余国良 不,这不是一个有效的实现。https://eel.is/c++draft/basic#intro.object-9.sentence-1要求对象的地址是其占用的第一个字节的地址。用于 vtable 指针的内存是对象的一部分。 (3认同)