我想实现像 rust 的 dyn 特性这样的东西(我知道这不适用于多重继承)
\n\n\nRun Code Online (Sandbox Code Playgroud)\ntemplate<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
但我发现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 无法证明
\nTrait<void>* operator->(){ return static_cast<Trait<void>*>(reinterpret_cast<void*>(_buffer)); }\nRun Code Online (Sandbox Code Playgroud)\n其他尝试
\n抽象类不能是联合成员(为什么?),并且我不能使用放置 new 在编译时计算偏移量,因为 Trait 是一个抽象类。
\n那么标准是否规定了 static_cast<T*>(ptr) == new (ptr) T() 的有效性呢?
\n表达式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通过继承或简单调整(例如更改符号或添加const或volatile)来关联它,并且由于多重继承,该地址可能会应用偏移量。但这在这个问题的代码中永远不会发生。