這對共享指針並不棘手。我們甚至可以避免多個分配。
struct any_block {
any_block(any_block const&)=delete;
template<class T>
T* try_get() {
if (!info || !ptr) return nullptr;
if (std::type_index(typeid(T)) != std::type_index(*info)) return nullptr;
return static_cast<T*>(ptr);
}
template<class T>
T const* try_get() const {
if (!info || !ptr) return nullptr;
if (std::type_index(typeid(T)) != std::type_index(*info)) return nullptr;
return static_cast<T const*>(ptr);
}
~any_block() {
cleanup();
}
protected:
void cleanup(){
if (dtor) dtor(this);
dtor=0;
}
any_block() {}
std::type_info const* info = nullptr;
void* ptr = nullptr;
void(*dtor)(any_block*) = nullptr;
};
template<class T>
struct any_block_made:any_block {
std::aligned_storage_t<sizeof(T), alignof(T)> data;
any_block_made() {}
~any_block_made() {}
T* get_unsafe() {
return static_cast<T*>((void*)&data);
}
template<class...Args>
void emplace(Args&&...args) {
ptr = ::new((void*)get_unsafe()) T(std::forward<Args>(args)...);
info = &typeid(T);
dtor = [](any_block* self){
static_cast<any_block_made<T>*>(self)->get_unsafe()->~T();
};
}
};
template<class D>
struct any_block_dtor:any_block {
std::aligned_storage_t<sizeof(D), alignof(D)> dtor_data;
any_block_dtor() {}
~any_block_dtor() {
cleanup();
if (info) dtor_unsafe()->~D();
}
D* dtor_unsafe() {
return static_cast<D*>((void*)&dtor_data);
}
template<class T, class D0>
void init(T* t, D0&& d) {
::new((void*)dtor_unsafe()) D(std::forward<D0>(d));
info = &typeid(T);
ptr = t;
dtor = [](any_block* s) {
auto* self = static_cast<any_block_dtor<D>*>(s);
(*self->dtor_unsafe())(static_cast<T*>(self->ptr));
};
}
};
using any_ptr = std::shared_ptr<any_block>;
template<class T, class...Args>
any_ptr
make_any_ptr(Args&&...args) {
auto r = std::make_shared<any_block_made<T>>();
if (!r) return nullptr;
r->emplace(std::forward<Args>(args)...);
return r;
}
template<class T, class D=std::default_delete<T>>
any_ptr wrap_any_ptr(T* t, D&& d = {}) {
auto r = std::make_shared<any_block_dtor<std::decay_t<D>>>();
if (!r) return nullptr;
r->init(t, std::forward<D>(d));
return r;
}
你必須執行any_cast
,但try_get<T>
應該很容易。
可能有一些像上面沒有處理的const T
這樣的角落案例。
template<class T>
std::shared_ptr<T>
crystalize_any_ptr(any_ptr ptr) {
if (!ptr) return nullptr;
T* pt = ptr->try_get<T>();
if (!pt) return nullptr;
return {pt, ptr}; // aliasing constructor
}
這可以讓你把一個any_ptr
並把它變成一個shared_ptr<T>
如果類型匹配而不復制任何東西。
live example。
您會注意到any_block_made
和any_block_dtor
有多相似。我相信這就是爲什麼std庫中至少有一個主要的shared_ptr重用了刪除者爲make_shared本身所處的位置。
我大概可以做類似的事情,並在這裏減少二進制大小。另外,any_block_made
和any_block_dtor
的T
/D
參數實際上只是關於我們所玩的內存塊的大小和對齊方式,以及我在親本指針dtor
中存儲的確切類型的擦除輔助程序。使用COMDAT摺疊(MSVC或GOLD)的編譯器/鏈接器可能會消除此處的二進制膨脹,但有一點小心,我可以自己做。
寫你自己的'任何'?這是所有類型擦除中最簡單的,所以這是一個很好的練習。 – Barry
@Barry只是想知道在Boost或其他地方是否有任何現有的解決方案。 – Anton3
你可以使用'boost :: variant',它也是更安全的類型。 –