虽然没有人真的应该使用这个,但是...
template <typename>
struct None1 {};
template <typename>
struct None2 {};
template <typename T>
struct PrivateBase { using Tpriv = T; using Tprot = None1<T>; using Tpub = None2<T>; };
template <typename T>
struct ProtectedBase { using Tpriv = None1<T>; using Tprot = T; using Tpub = None2<T>; };
template <typename T>
struct PublicBase { using Tpriv = None1<T>; using Tprot = None2<T>; using Tpub = T; };
template <typename K>
struct TriBase : private K::Tpriv, protected K::Tprot, public K::Tpub {};
template <typename T, typename ... Bases>
struct Sized : private Bases::Tpriv..., protected Bases::Tprot..., public Bases::Tpub...
{
virtual size_t size() { return sizeof(T); }
};
struct Foo : Sized<Foo> {};
struct X{};
struct Y{};
struct Bar : Sized<Bar, PrivateBase<X>, ProtectedBase<Y>, PublicBase<Foo>> {};
int main ()
{
Bar b;
Foo* f = &b;
X* x = &b;
Y* y = &b;
}
虚拟继承留给读者作为练习。
基类的顺序不保留,但您不应该依赖它。
可以像这样实现稍微更适合生产环境的内容(这是一个粗略的草图):
#include <cstdlib>
#include <typeinfo>
#include <unordered_map>
#include <memory>
#include <iostream>
struct myinfo
{
size_t size;
};
using TypeInfoRef = std::reference_wrapper<const std::type_info>;
struct Hasher
{
std::size_t operator()(TypeInfoRef code) const
{
return code.get().hash_code();
}
};
struct EqualTo
{
bool operator()(TypeInfoRef lhs, TypeInfoRef rhs) const
{
return lhs.get() == rhs.get();
}
};
static std::unordered_map<TypeInfoRef, myinfo, Hasher, EqualTo> typemap;
template <typename K>
struct typemap_initializer
{
typemap_initializer()
{
typemap[typeid(K)] = myinfo{sizeof(K)};
}
};
struct Base
{
virtual ~Base() {}
size_t size() { return typemap[typeid(*this)].size; }
template<typename K, typename... Arg>
friend K* alloc(Arg...);
private:
void* operator new(size_t sz) { return ::operator new(sz); }
};
template<typename K, typename... Arg>
K* alloc(Arg... arg)
{
static typemap_initializer<K> ti;
return new K(arg...);
}
struct Foo : Base {int a;};
struct Bar : Foo {int b; int c;};
int main ()
{
Foo* f = alloc<Foo>();
Bar* g = alloc<Bar>();
std::cout << f->size() << std::endl;
std::cout << g->size() << std::endl;
}
当然,这意味着我们放弃了熟悉的
Foo* foo = new Foo
语法,但在普及的
std::make_shared<>
时代,这不是一个大问题。
typeid
获取标识),例如类型名称(type_info::name()
已损坏)或类似于通过复制构造函数定义的clone()
。 - yuri kilochekBase
和Derive
等不直接从Sized
派生,而只是临时派生。例如,如果您在代码中的某个地方需要一个类型派生自SizedBase
的对象,则将其包装在其中。是否可能/有效取决于这些类型的移动构造函数的速度。 - dypstruct SizedBase { virtual size_t size() const = 0; }; template <class T> struct Sized : SizedBase, T { template <class... Args> Sized(Args.. args) : T(args...) {} size_t size() const { return sizeof(*this); } }; template <class T, class... Args> T* get(Args... args) { return new (alloc.get(sizeof(Sized<T>))) Sized<T>(args...); } template <class T> void put(T* p) { size_t s = dynamic_cast<SizedBase*>(p)->size(); alloc.put(p, s); }
当“T”为“final”时很遗憾它失败了。 - yuri kilochekfinal
用于类是我知道的唯一一个我还不确定有用途的特性;请参见 http://akrzemi1.wordpress.com/2012/09/30/why-make-your-classes-final/ 如果类是 final 的话,你可以选择使用额外的 vptr。 - dyp