我希望能够获得多态对象的大小。此刻我得到了这个:
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
来自 Sized<Derived>
.我们可以通过通过继承来解决这个问题 Sized
:
struct SizedBase {
virtual std::size_t size() const = 0;
};
template <typename Type, typename... SizedBases>
struct Sized : virtual SizedBase, SizedBases... {
std::size_t size() const override {
return sizeof(Type);
}
};
struct Base : Sized<Base> {};
struct Derived : Sized<Derived, Base> {};
这按预期工作,但是在多重继承的情况下会有些困惑,并且禁止更改基的可访问性/虚拟性。
那么,有没有更好的办法呢?
最佳答案
并不是说任何人都应该真正使用它,但是......
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<>
这不是什么大问题。
关于c++ - 获取多态对象的大小,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/25961717/