我希望能够获得多态对象的大小。目前我得到了这个:
struct Base {
virtual std::size_t size() const {
return sizeof(*this);
}
};
struct Derived : Base {
virtual std::size_t size() const {
return sizeof(*this);
}
};
这是字面上的副本&糊。我想做得更好。假设我真的讨厌宏,而CRTP似乎是唯一合理的方法。我们试一试:
struct SizedBase {
virtual std::size_t size() const = 0;
};
template <typename Type>
struct Sized : virtual SizedBase {
std::size_t size() const override {
return sizeof(Type);
}
};
struct Base : Sized<Base> {};
struct Derived : Base, Sized<Derived> {};
这看起来好多了,但遗憾的是格格不入:Derived
包含来自size()
和Base
的{{1}}的两个最终重写 。我们可以通过继承到 Sized<Derived>
:
Sized
这可以按预期工作,但是在多重继承的情况下会有些混乱,并且禁止更改基础的可访问性/虚拟性。
那么,有更好的方法吗?
答案 0 :(得分:1)
并非任何人都应该真的使用它,但是......
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; // error : private base
Y* y = &b; // error : protected base
}
虚拟继承留给读者练习。
不保留基类的顺序,但不管怎样你都不应该依赖它。
有点像生产友好的东西可以这样实现(这是一个粗略的草图):
#include <cstdlib>
#include <typeinfo>
#include <unordered_map>
#include <memory>
#include <iostream>
struct myinfo
{
size_t size;
// any other stuff
};
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<>
时代,这不是一个大问题。