没有新成员的模板化多态派生的大小 类

Size of templated polymorphic derived classes without new members

假设我有以下结构:

template <typename T>
struct Wrapper {
    virtual T* get() const = 0;
protected:
    void *c_;
};

template <typename C, typename T>
struct WrapperOf: Wrapper<T> {

    WrapperOf(C *c = 0) : c_(c) { }

    virtual T* get() const {
        C *c = static_cast<C*>(c_);
        return static_cast<T*>(c->get());
    }
};

标准是否保证任何WrapperOf的大小都相同?通常,我可以执行以下操作:

struct Dummy { void* get(); };
struct Real { int* get(); };

char storage[sizeof(WrapperOf<Dummy, void>)];
Wrapper<int> *wp = 
    new(storage) WrapperOf<Real, int>();

如果我专攻 WrapperOf,通常:

template <>
struct WrapperOf<void, void>: Wrapper<void> {
    virtual void* get() const { return 0; }
};

使用它来初始化存储(避免Dummy class):

char storage[sizeof(WrapperOf<void, void>)];

这仍然有效吗?

不,在标准中任何地方都不能保证,因为除了一些狭窄的情况外,标准并没有真正保证对象的大小。

还值得注意的是,标准没有说明任何有关虚函数实现的内容(标准中不存在诸如 vptr 之类的东西)。

唯一可以保证大小的类型是标准布局类型。多态性(以及其他)特别排除了这一点。

然而,你可以做你想做的事(我假设你想分配足够的 space 来创建任何类型的一组 WrapperOf):

#include <memory>
#include <tuple>

struct Dummy { void* get(); };
struct Real { int* get(); };
struct UnReal { float* get(); };

template <typename T>
struct Wrapper {
    Wrapper(void* c) : c_(c) {}
    virtual T* get() const = 0;
    void* get_c() { return c_; }
    void* get_c() const { return c_; }
protected:
    void *c_;
};


template <typename C, typename T>
struct WrapperOf: Wrapper<T> {

    WrapperOf(C *c = 0) : Wrapper<T>(c) { }

    virtual T* get() const {
        C *c = static_cast<C*>(this->get_c());
        return static_cast<T*>(c->get());
    }
};

template <>
struct WrapperOf<void, void>: Wrapper<void> {
    virtual void* get() const { return 0; }
};

template<class Type, class...Rest>
struct largest_of
{
    static constexpr auto x = sizeof(Type);
    static constexpr auto y = largest_of<Rest...>::size;
    static constexpr std::size_t size = x > y ? x : y;

    static constexpr auto q = alignof(Type);
    static constexpr auto p = largest_of<Rest...>::alignment;
    static constexpr std::size_t alignment = q > p ? q : p;
};

template<class T> struct largest_of<T>
{
    static constexpr std::size_t size = sizeof(T); 
    static constexpr std::size_t alignment = alignof(T);
};

template<class...Ts> struct largest_of<std::tuple<Ts...>> {
    static constexpr std::size_t size = largest_of<Ts...>::size; 
    static constexpr std::size_t alignment =largest_of<Ts...>::alignment;
};

using candidates = std::tuple<
    WrapperOf<Real, int>,
    WrapperOf<UnReal, float>,
    WrapperOf<Dummy, void>,
    WrapperOf<void, void>
>;

using largest = largest_of<candidates>;
std::aligned_storage<largest::size, largest::alignment> storage;

int main()
{
    auto p1 = new (std::addressof(storage)) WrapperOf<Real,int>();
    p1->~WrapperOf<Real,int>();

    auto p2 = new (std::addressof(storage)) WrapperOf<UnReal, float>();
    p2->~WrapperOf<UnReal,float>();
}

来自当前工作草案N4640 (2017-02-06)

5.3.3 Sizeof [expr.sizeof]

  1. The sizeof operator yields the number of bytes in the object representation of its operand.

  2. ... When applied to a class, the result is the number of bytes in an object of that class including any padding required for placing objects of that type in an array.

所以没有任何保证,只是它需要多少字节。

即使对于大多数基本类型,标准也表示它是 实现定义的

  1. ... sizeof(char), sizeof(signed char) and sizeof(unsigned char) are 1. The result of sizeof applied to any other fundamental type (3.9.1) is implementation-defined.

可以推断某些 class 需要 N 个字节,并且根据经验可以看出,在给定的实现中,它对于一系列派生的 class 是相同的。只是没有保证。