Si vous avez une méthode d'usine en place qui construira l'objet dans un bloc de mémoire que vous fournissez, plus une méthode qui vous indique la quantité d'espace dont vous aurez besoin, vous pouvez le faire.
Je vais utiliser librement C++ 14, ajouter vos propres typename
et ::type
si vous avez vraiment besoin de C++ 11.
D'abord, nous supposons que nous avons ceci:
struct some_args {
// whatever
};
std::size_t how_big_is_X(some_args);
X* make_X(some_args, void* buffer);
avec ce qui précède, je peux faire quelque chose fonctionnellement équivalent à make_shared
.
template<std::size_t Sz, std::size_t align=alignof(void*)>
struct smart_buffer_t {
void(*dtor)(void*) = 0;
std::aligned_storage_t< Sz, align> data;
template<class T, class...Args>
T* emplace(Args&&...args) {
return ctor([&](void* pdata) {
::new(pdata) T(std::forward<Args>(args)...);
});
}
template<class F>
T* ctor(F&& f) {
std::forward<F>(f)((void*)&data);
dtor = [](void* ptr){
static_cast<T*>(ptr)->~T();
};
return static_cast<T*>((void*)&data);
}
~smart_buffer_t() {
if (dtor) dtor(&data);
}
};
template<class T, std::size_t Sz, std::size_t Algn=alignof(void*), class F>
std::shared_ptr<T>
make_sized(F&& f) {
auto pbuff = std::make_shared<smart_buffer_t<Sz, Algn>>();
T* r = pbuff->ctor(std::forward<F>(f));
return {r, pbuff}; // aliasing ctor
}
nous avons maintenant:
template<std::size_t I>
using index_t = std::integral_constant<std::size_t, I>;
template<std::size_t I>
using pow_t = index_t< (1<<I) >;
std::shared_ptr<X>
make_shared_X(some_args args) {
std::size_t Sz = how_big_is_X(args);
using pmaker = std::shared_ptr<X>(*)(some_args);
using maker_maker = [](auto Sz){
return +[](some_args args) {
return make_sized<X, Sz>([&](void* ptr){
return make_X(args, ptr);
});
};
};
static const pmaker table[] = {
maker_maker(pow_t<0>{}),
maker_maker(pow_t<1>{}),
maker_maker(pow_t<2>{}),
// ...
maker_maker(pow_t<63>{}), // assuming 64 bit size_t.
};
std::size_t i = 0;
while(Sz > (1<<i))
++i;
return table[i](args);
}
ou somesuch.
Code non testé. Il alloue la puissance de 2 à ou plus que la demande de votre args. Mais l'objet est construit dans la même allocation que le bloc de comptage de référence.
Toute série au lieu de puissances de 2 peut être utilisée, mais la taille de la table doit être suffisamment grande pour gérer la plus grande valeur de retour possible de how_big_is_X
.
Que voulez-vous dire par la « taille » de l'objet? Vous ne pouvez pas avoir des instances de la même classe qui diffèrent en taille. – Quentin
« Size » est la longueur en octets de la mémoire contiguë qui est alloué lorsqu'un objet est construit, et libéré quand elle est détruite. –
Pourriez-vous éditer dans un code montrant comment vous créez ces objets * sans * pointeurs intelligents? – Quentin