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

时间:2017-04-21 13:53:06

标签: c++ templates polymorphism sizeof placement-new

假设我有以下结构:

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类):

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

这仍然有效吗?

3 个答案:

答案 0 :(得分:1)

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

值得注意的是,标准对虚拟功能实现没有任何说明(标准中不存在vptr之类的东西)。

答案 1 :(得分:1)

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

但是,你可以做你想要的(我假设你想分配足够的空间来创建任何一种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>();
}

答案 2 :(得分:1)

从当前的工作草案N4640(2017-02-06)

  

5.3.3 Sizeof [expr.sizeof]

     
      
  1. sizeof运算符产生其操作数的对象表示中的字节数。

  2.   
  3. ......申请时   对于一个类,结果是该类对象中的字节数,包括在数组中放置该类型对象所需的任何填充。

  4.   

所以没有任何保证,只需要花费很多字节就可以了。

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

  
      
  1. ... sizeof(char),sizeof(signed char)和sizeof(unsigned char)是1.应用于任何其他基本类型(3.9.1)的sizeof的结果是实现定义的。
  2.   

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