I want to implement something like rust's dyn trait(I know this doesn't work for multiple inheritance)
template<template<typename> typename Trait> class Dyn { struct _SizeCaler:Trait<void>{ void* _p;}; char _buffer[sizeof(_SizeCaler)]; public: template<typename T> Dyn(T* value){ static_assert(std::is_base_of_v<Trait<void>,Trait<T>> ,"error Trait T,is not derive from trait<void>" ); static_assert(sizeof(_buffer) >= sizeof(Trait<T>) ,"different vtable imple cause insufficient cache" ); new (_buffer)Trait<T>{value}; } Trait<void>* operator->(){ return static_cast<Trait<void>*>(reinterpret_cast<void*>(_buffer)); } }; template<template<typename> typename Trait,typename T> struct DynBase:Trait<void> { protected: T* self; public: DynBase(T* value):self(value){} }; struct Point{ double x,y; }; struct Rect{ Point m_leftDown,m_rightUp; Rect(Point p1,Point p2){ m_leftDown = Point{std::min(p1.x,p2.x),std::min(p1.y,p2.y)}; m_rightUp = Point{std::max(p1.x,p2.x),std::max(p1.y,p2.y)}; } }; template<typename = void> struct Shape; template<> struct Shape<void> { virtual double area() = 0; }; template<> struct Shape<Rect> final :DynBase<Shape,Rect> { using DynBase<Shape,Rect>::DynBase; double area() override{ return (self->m_rightUp.x - self->m_leftDown.x ) * (self->m_rightUp.y - self->m_leftDown.y); } }; void printShape(Dyn<Shape> ptr) { std::cout << ptr->area(); } int main() { Rect r{{1,2},{3,4}}; printShape(&r); }
but I found that the c++standard may not be able to derive “new (ptr) T() == static_cast<T*>(ptr)“? So conversely, “static_cast<T*>(ptr) == new (ptr) T()” cannot prove
Trait<void>* operator->(){ return static_cast<Trait<void>*>(reinterpret_cast<void*>(_buffer)); }
Other attempts
a abstruct class can't be a union member(why?),and I can't use placement new to calculate offset at compile time, because Trait is an abstract class.
So does the standard specify the validity of static_cast<T*>(ptr) == new (ptr) T()?
The expressions new (ptr) T()
and static_cast<T*>(ptr)
, where ptr
has type void*
, will return the same address, ptr
(as long as T
is a scalar type -- array types are allowed to have overhead when dynamically allocated)
However, the semantics are quite different.
In new (ptr) T()
, a new object of type T
is created at the specified address. Any prior value is lost.
In static_cast<T*>(ptr)
, there must already be an object of type T
at the specified address, or else you are setting yourself up to violate the strict aliasing rule.
If ptr
had a type other than void*
, it would need to be related to T
by inheritance or trivial adjustments (such as changing signedness or adding const
or volatile
), and the address might have an offset applied as a result of multiple inheritance. But that will never happen in the code of this question.