我想實作類似 rust 的 dyn 特征(我知道這不適用于多重繼承)
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); }
但是我發現c 標準可能無法匯出“new (ptr) T() == static_cast<T*>(ptr)”?所以反過來說,“static_cast<T*>(ptr) == new (ptr) T()”不能證明
Trait<void>* operator->(){ return static_cast<Trait<void>*>(reinterpret_cast<void*>(_buffer)); }
其他嘗試
一個抽象類不能是一個聯合成員(為什么?),我不能在編譯時使用placement new來計算偏移量,因為Trait是一個抽象類。
那么標準是否指定了 static_cast<T*>(ptr) == new (ptr) T() 的有效性?
uj5u.com熱心網友回復:
具有型別的運算式new (ptr) T()andstatic_cast<T*>(ptr)將回傳相同的地址(只要是標量型別——動??態分配時允許陣列型別有開銷)ptrvoid*ptrT
但是,語意完全不同。
在中,在指定地址創建
new (ptr) T()一個新的型別物件。T任何先前的價值都會丟失。在中,指定地址
static_cast<T*>(ptr)必須已經有一個型別的物件T,否則您將自己設定為違反嚴格的別名規則。
如果ptr具有除 之外的型別void*,則需要T通過繼承或微不足道的調整(例如更改符號或添加constor volatile)將其關聯,并且地址可能由于多重繼承而應用了偏移量。但這永遠不會在這個問題的代碼中發生。
uj5u.com熱心網友回復:
是否new (ptr) T() == static_cast<T*>(ptr)屬實不是你的實際問題。
實際問題是Trait<T>您正在構建的類不是標準布局,因此Trait<void>物件的子Trait<T>物件既不能與物件進行指標互轉換Trait<T>,也不必位于同一地址。因此,在不知道或沒有具體指向的情況下,您無法從_bufferto轉換。Trait<void>*TTrait<void>
換句話說,您需要在從(通過隱式轉換)Trait<void>*的結果初始化的類中存盤一個附加指標new (_buffer)Trait<T>{value};,或者至少在Trait<T>其Trait<void>子物件之間存盤一個偏移量。(在后一種情況下,請注意您需要std::launder在 . 之后添加一個reinterpret_cast。)
此外,正如評論中提到的,您需要確保緩沖區針對 type 的物件進行了適當的對齊Trait<T>。這需要alignas在緩沖區上添加一個,以便它適合T您想要考慮的所有內容,然后在對齊上可能也應該有一個static_assert類似于大小的對齊。
另外,static_cast<Trait<void>*>(reinterpret_cast<void*>(_buffer))等價于reinterpret_cast<Trait<void>*>(_buffer)。不需要雙重演員。
uj5u.com熱心網友回復:
其他問題:
抽象類不能是聯合成員(為什么?)
因為將抽象類作為聯合成員是沒有意義的。為了使用聯合成員,您需要實體化一個聯合成員型別的物件以存盤在聯合中。但是您不能實體化抽象類的物件,因此您永遠不能將抽象類聯合成員用于任何事情。
轉載請註明出處,本文鏈接:https://www.uj5u.com/ruanti/521609.html
標籤:C 标准
上一篇:使用C 前處理器進行簡單計算
