在编译时确定最大的派生类

时间:2015-04-14 22:23:29

标签: c++ inheritance

我们说我有一个班级Base,其中有N个孩子,Derived0, Derived1, ... ,DerivedN。我正在尝试为从Base继承的对象创建池分配器,并且实现依赖于知道Base的最大子节点的大小,因为每个池都足够大以包含其中一个对象。这是一个简单的例子,包含普通的类和N = 2。实际上,N可能会更大,Base的孩子可能不是简单的POD课程。

class Base {
public:
    virtual ~Base() = 0;
};

class Derived0 : public Base {
    int a;
};

class Derived1 : public Base {
    int a, b;
};

class Derived2 : public Base {
    int a, b, c;
};

所以,让我们说我想创建一个足够大的缓冲区来存储10个孩子。我已经在这上面敲了一会儿,这就是我能想到的:

static const size_t NUMBER_OF_POOLS = 10;

static const size_t LARGEST_CHILD_SIZE =
    sizeof(Derived0) > sizeof(Derived1) ?
        sizeof(Derived0) > sizeof(Derived2) ?
            sizeof(Derived0) :
            sizeof(Derived2) :
        sizeof(Derived1) > sizeof(Derived2) ?
            sizeof(Derived1) :
            sizeof(Derived2);

char buffer[NUMBER_OF_POOLS * LARGEST_CHILD_SIZE];

这很有效,但随着N开始增长,很容易看出它变得多么混乱。有没有任何可扩展的方法来实现这一点,你不需要手动构建一个嵌套的"一元树" (因为缺乏一个更好的术语)随着N的增加而变成一个巨大的混乱?

以下是我正在使用的约束/环境:

  • 在编译时必须知道最大的孩子的大小
  • C ++ 03兼容性是必要的。为了好奇,我对任何建议(C ++ 11欢迎!)感兴趣,但我最终会在没有C ++ 11支持的编译器上构建它。我知道C ++ 11增加了对非POD成员的联合的支持,所以我认为你可能能够使用它来清理界面。
  • 这只能使用GCC构建,因此GCC扩展在技术上是一种选择。

任何帮助/建议表示赞赏。

3 个答案:

答案 0 :(得分:3)

您可以考虑使用boost::variant并使用其大小:

    sizeof (boost::variant< Derived0, Derived1, Derived2 > )  

这将返回最大元素的大小。

顺便说一句,它还可以简化缓冲区管理,使其成为此变体的缓冲区,并直接索引正确的元素。好消息是,提升需要考虑对齐要求:

    typedef boost::variant< Derived0, Derived1, Derived2 > DerivedVar; 
    DerivedVar buffer[NUMBER_OF_POOLS];  

答案 1 :(得分:3)

仅适用于POD课程:

union AllDerived {
    Derived0 _0;
    Derived1 _1;
    Derived2 _2;
};

static const size_t LARGEST_CHILD_SIZE = sizeof(AllDerived);
static const size_t NUMBER_OF_POOLS = 10;
char buffer[NUMBER_OF_POOLS * LARGEST_CHILD_SIZE];

此解决方案不仅适用于POD:

template <int Value1, int Value2>
struct static_max {
    static const int value = (Value1 < Value2) ? Value2 : Value1 ;
};

template<typename T, typename U>
struct TypeList {
    typedef T Head;
    typedef U Tail;
};

class NullType {};

template
<
    typename T1  = NullType, typename T2  = NullType, typename T3  = NullType,
    typename T4  = NullType, typename T5  = NullType, typename T6  = NullType,
    typename T7  = NullType, typename T8  = NullType, typename T9  = NullType,
    typename T10 = NullType, typename T11 = NullType, typename T12 = NullType,
    typename T13 = NullType, typename T14 = NullType, typename T15 = NullType,
    typename T16 = NullType, typename T17 = NullType, typename T18 = NullType
>
struct MakeTypelist
{
private:
    typedef typename MakeTypelist
    <
        T2 , T3 , T4 ,
        T5 , T6 , T7 ,
        T8 , T9 , T10,
        T11, T12, T13,
        T14, T15, T16,
        T17, T18
    >
    ::Result TailResult;

public:
    typedef TypeList<T1, TailResult> Result;
};

template<>
struct MakeTypelist<>
{
    typedef NullType Result;
};

template<typename TList>
struct MaxTypeSize;

template <>
struct MaxTypeSize<NullType> {
    enum { value=0 };
};

template<typename T, typename U>
struct MaxTypeSize<TypeList<T,U>> {
    enum { value = static_max<sizeof(T), MaxTypeSize<U>::value>::value };
};

typedef MakeTypelist<Derived0, Derived1, Derived2>::Result AllTypes;
static const size_t LARGEST_CHILD_SIZE = MaxTypeSize<AllTypes>::value;
static const size_t NUMBER_OF_POOLS = 10;
char buffer[NUMBER_OF_POOLS * LARGEST_CHILD_SIZE];

这里我们使用类型列表和编译类型的最大函数。您可以在Loki库中找到类型列表实现。编译时函数MaxTypeSize计算列表中的最大类型大小。

答案 2 :(得分:1)

通过引入辅助宏来修改代码,从而产生更好的代码格式。添加新的派生类后,只需插入STATIC_MAX(sizeof(...行并添加)即可。这适用于C ++ 03。

#define STATIC_MAX(a, b)  ((a) > (b) ? (a) : (b))

static const size_t LARGEST_CHILD_SIZE =
    STATIC_MAX(sizeof(Derived0),
    STATIC_MAX(sizeof(Derived1),
    STATIC_MAX(sizeof(Derived2),
               0)));

如果C ++ 1x编译器是一个选项,您可以使用“variadic template”和“constexpr function”来获得更简单的解决方案。添加新的派生类时,只需在最后一行插入类名作为模板参数。

template <typename T>
static constexpr T static_max(T a, T b) {
    return a < b ? b : a;
}

template <typename T, typename... Ts>
static constexpr T static_max(T a, Ts... bs) {
    return static_max(a, static_max(bs...));
}

template <typename... Ts>
constexpr size_t max_sizeof() {
    return static_max(sizeof(Ts)...);
};

static constexpr size_t LARGEST_CHILD_SIZE =
    max_sizeof<Derived0, Derived1, Derived2>();