Reputation: 117701
Take a look at these three classes (includes ommited):
template<class T> struct A {
std::allocator<T> allocator;
T* ptr;
};
template<class T> struct B {
T* ptr;
std::allocator<T> allocator;
};
template<class T> struct C : std::allocator<T> {
T* ptr;
};
int main(int argc, char **argv) {
std::cout << "A: " << sizeof(A<int>) << "\n";
std::cout << "B: " << sizeof(B<int>) << "\n";
std::cout << "C: " << sizeof(C<int>) << "\n";
}
If you'd ask me, depending on alignment, either A
or B
has to have the same size as C
. However, printing the sizeof
claims otherwise:
A: 16
B: 16
C: 8
Why is this so?
Upvotes: 1
Views: 94
Reputation: 477100
By C++11, 9/4:
Complete objects and member subobjects of class type shall have nonzero size.
No such restriction applies to base classes, as long as every object has a unique pair of type and address. So as long as the "first" data member hasn't got the same type as a base subobject, the base subobject may have zero size, being neither complete nor a member.
(I put "first" in quotation marks since there's a complication involving access levels.)
In fact, 1.8/5–6 formalize the above:
5 Unless it is a bit-field (9.6), a most derived object shall have a non-zero size and shall occupy one or more bytes of storage. Base class subobjects may have zero size. An object of trivially copyable or standard-layout type (3.9) shall occupy contiguous bytes of storage.
6 Unless an object is a bit-field or a base class subobject of zero size, the address of that object is the address of the first byte it occupies. Two objects that are not bit-fields may have the same address if one is a subobject of the other, or if at least one is a base class subobject of zero size and they are of different types; otherwise, they shall have distinct addresses.
Here's a "typical" implementation of std::vector
, minus all the name mangling:
template <typename T, typename Alloc>
class vector
{
struct vbase : Alloc
{
T * data, * end, * capacity;
vbase(Alloc const & a) : Alloc(a), data(), end(), capacity() { }
};
vbase impl;
public:
vector(Alloc const & a = Alloc()) : impl(a) { }
T * data() const { return impl.data; }
T & operator[](size_t n) { return data()[n]; }
// ...
// use impl.allocate(), impl.construct() etc.
};
This basically ensures that sizeof(vector<T>) == 3 * sizeof(T*)
whenever the allocator is empty.
Upvotes: 5