没有新成员的模板化多态派生的大小 类
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]
The sizeof operator yields the number of bytes in the object representation of its operand.
... 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.
所以没有任何保证,只是它需要多少字节。
即使对于大多数基本类型,标准也表示它是 实现定义的
- ... 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 是相同的。只是没有保证。
假设我有以下结构:
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]
The sizeof operator yields the number of bytes in the object representation of its operand.
... 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.
所以没有任何保证,只是它需要多少字节。
即使对于大多数基本类型,标准也表示它是 实现定义的
- ... 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 是相同的。只是没有保证。