In this code, I get a different sizeof(T) if the allocator is part of a container allocation:
#include <iostream>
#include <set>
#include <cstddef>
class Item
{
int a;
unsigned char b, c, d;
int e, f, g;
public:
Item() { a = b = c = d = e = f = g = 0; }
bool operator<(const Item& item) const { return item.a < a; }
};
template <typename T> class TestAllocator
{
public:
typedef T value_type;
typedef size_t size_type;
typedef ptrdiff_t difference_type;
typedef T* pointer;
typedef const T* const_pointer;
typedef T& reference;
typedef const T& const_reference;
pointer address(reference x) const { return &x; }
const_pointer address(const_reference x) const { return &x; }
TestAllocator() { std::cout << "TestAllocator ctor: sizeof T:" << sizeof(T) << std::endl; }
template <typename U> TestAllocator(const TestAllocator<U>&) {}
~TestAllocator() {}
pointer allocate(size_type /*n*/, void * = 0) { return static_cast<T*>(new T()); }
void deallocate(pointer p, size_type /*n*/) { delete p; }
TestAllocator<T>& operator=(const TestAllocator&) { return *this; }
void construct(pointer p, const T& val) { new ((T*) p) T(val); }
void destroy(pointer p) { p->~T(); }
size_type max_size() const { return size_t(-1); }
template <typename U> struct rebind { typedef TestAllocator<U> other; };
template <typename U> TestAllocator& operator=(const TestAllocator<U>&) { return *this; }
};
typedef std::multiset<Item, std::less<Item>, TestAllocator<Item> > ItemMultiset;
int main(int /*argc*/, char** /*argv*/)
{
std::cout << "Instantiating allocator:" << std::endl;
TestAllocator<Item> ta;
std::cout << "Instantiating container:" << std::endl;
ItemMultiset ims;
return 0;
}
Here on my gcc 7.2.1, I get:
Instantiating allocator:
TestAllocator ctor: sizeof T:20
Instantiating container:
TestAllocator ctor: sizeof T:56
Some online compilers results:
VC++ at webcompiler.cloudapp.net said 20 and 36.
Coliru at coliru.stacked-crooked.com said 20 and 56 for all selected gcc compilers, 20 and 56 for clang 3.8, or 20 and 48 for clang 3.8 C++11/14.
Why the difference, and why do some results pad every struct member?
How can I ask what alignment 'mode' the container is in and apply it to my struct or code, or else how can I tell the container to use my code's mode, to ensure the results are always identical?
EDIT: Thanks for the fast reply below.
Wow, a lot of space used. Further results with other containers:
Instantiating allocator:
TestAllocator ctor: sizeof T:20
Instantiating multiset:
TestAllocator ctor: sizeof T:56
Instantiating multimap:
TestAllocator ctor: sizeof T:20
Instantiating list:
TestAllocator ctor: sizeof T:40
Instantiating vector:
TestAllocator ctor: sizeof T:20
EDIT 2:
For the benefit of those working with allocation pools:
Yay! I think I achieved my goal. The sample code is based
on a real app and, as you might expect, the allocator template's
allocate
and deallocate
don't just call new
and delete
.
they hand off to a pool. Until Thursday the pool was a global
chunking style multi-dimensional (several different planes
for common expected size requests). allocate
would pass
the number of bytes required. Then I template-ized our
global pool, but somewhat clumsily the global instance had to
be separately initialized with the desired type - that's where
the trouble started, that's not the right type! I saw an opportunity
for allocate
to pass only the number of items instead of bytes.
As you saw it didn't work the way I tried. My mistake was that
so soon after template-izing our pool, I didn't realize I could
just drop a static instance of it in my allocator template class.
Boom, problem solved, all the sizeof's are consistent now. The pool
is working fine now, it is now a template embedded into the
allocator template class, and it is more lean and efficient than our
previous version. ~25 years with C++, templates never cease to amaze me. Thanks for your help.