could/should allocator<T> be a static member?

B

Bernhard Kick

Hi all,

I saw this code in the book "Accelerated C++" (chapt 11, iirc):

template <class T> class Vec {
...
std::allocator<T> alloc; // object to handle memory allocation

// ??would static member work here??:
// static std::allocator<T> alloc;
...
};

If i understand this correctly, in the code above,
every instance of Vec<T> gets its own allocator.

Wouldn't it be better "style" if alloc was a _static_ member?
Then all instances of Vec<T> would share one common allocator.
Is a static allocator guaranteed to work?

Thanks for comments/insights,
Bernhard Kick.
 
P

P.J. Plauger

I saw this code in the book "Accelerated C++" (chapt 11, iirc):

template <class T> class Vec {
...
std::allocator<T> alloc; // object to handle memory allocation

// ??would static member work here??:
// static std::allocator<T> alloc;
...
};

If i understand this correctly, in the code above,
every instance of Vec<T> gets its own allocator.

Wouldn't it be better "style" if alloc was a _static_ member?
Then all instances of Vec<T> would share one common allocator.
Is a static allocator guaranteed to work?

Two considerations:

1) An allocator is often a zero-sized object. With proper
finagling, you can get it to occupy no space in each
vector object. Making it a static would then use *more*
space (however small), but also add a minor complication
to instantiating such a container in a DLL.

2) An allocator that is not a zero-sized object may well
have writable storage, for maintaining a per-container
pool, for example. The standard containers are not required
to accept such allocators, but the C++ Standard encourages
implementations to do so. The Dinkum C++ Library accepts
them. Making such an allocator static would then require
thread locking to avoid conflicts if more than one thread
is working with containers of the given type.

So it's probably not a good idea to make allocators static
under two circumstances:

1) if allocators are zero-sized objects

2) if allocators are not zero-sized objects

I won't comment on whether it would be better *style* to
do so.

P.J. Plauger
Dinkumware, Ltd.
http://www.dinkumware.com
 
B

Bernhard Kick

P.J. Plauger said:
Two considerations:

1) An allocator is often a zero-sized object. With proper
finagling, you can get it to occupy no space in each
vector object. Making it a static would then use *more*
space (however small), but also add a minor complication
to instantiating such a container in a DLL.

proper finagling? hmm...(had to look up the word in the dictionary)
2) An allocator that is not a zero-sized object may well
have writable storage, for maintaining a per-container
pool, for example. The standard containers are not required
to accept such allocators, but the C++ Standard encourages
implementations to do so. The Dinkum C++ Library accepts
them. ...

Aha! So std::allocator could have private state.
Then of course its a *big* difference whether you make it
static or not. Making it static could break the code. Correct?
So it's probably not a good idea to make allocators static
under two circumstances:

1) if allocators are zero-sized objects
2) if allocators are not zero-sized objects

The following little test seems to show that
non-static member takes space, but static does not.
(gcc under cygwin):

//------------------------------------------------------------------------
#include <memory>
#include <iostream>
using namespace std;

class small { int a; };
class big { int a[200]; };

class small_alloc {
small a;
allocator<small> alloc;
};

class small_alloc_static {
small a;
static allocator<small> alloc;
};

class big_alloc {
big a;
allocator<big> alloc;
};

class big_alloc_static {
big a;
static allocator<big> alloc;
};

int main(int argc, char *argv[]) {
cout << "size(alloc<small>)=" << sizeof(allocator<small>)
<< " size(alloc<big>)=" << sizeof(allocator<big>)
<< endl;

cout << "size(small)=" << sizeof(small)
<< " size(small_alloc)=" << sizeof(small_alloc)
<< " size(small_alloc_static)=" << sizeof(small_alloc_static)
<< endl;

cout << "size(big)=" << sizeof(big)
<< " size(big_alloc)=" << sizeof(big_alloc)
<< " size(big_alloc_static)=" << sizeof(big_alloc_static)
<< endl;

return 0;
}
//---------------------------------------------------------------

The result is:
size(alloc<small>)=1 size(alloc<big>)=1
size(small)=4 size(small_alloc)=8 size(small_alloc_static)=4
size(big)=800 size(big_alloc)=804 size(big_alloc_static)=800

From this i would conclude that:

1) the gcc std::allocator<T> is a zero-sized object.
(sizeof(allocator<T>) returns 1, but i cannot imagine it
really has 1 byte of private state.)

2) the static member alloc takes _no_ space, but the non-static does.
So I'd prefer to make it a static member, unless it breaks the code.

The result could also mean that gcc cannot do "proper finagling"
(whatever that means...would you care to explain?)


I won't comment on whether it would be better *style* to
do so.

No need to comment on style.

I am coming form the traditional malloc/free() world,
*one* global allocator for *all* types of objects.

Thanks very much for your technical explanations,
which help me to better understand what a C++ std::allocator is.

Bernhard Kick.
 
P

P.J. Plauger

Aha! So std::allocator could have private state.
Then of course its a *big* difference whether you make it
static or not. Making it static could break the code. Correct?

In the sense that it's a sucker for race conditions in a
multithreaded environment, yes.
The following little test seems to show that
non-static member takes space, but static does not.
(gcc under cygwin):

Of course. Non-static members exist in each object, while
static members are shared across all objects and have just
one instance.
From this i would conclude that:

1) the gcc std::allocator<T> is a zero-sized object.
(sizeof(allocator<T>) returns 1, but i cannot imagine it
really has 1 byte of private state.)

It doesn't. C++ generally requires that distinct objects have
distinct addresses, so the zero-sized object is given one
padding byte to distinguish it from whatever comes next.
2) the static member alloc takes _no_ space, but the non-static does.
So I'd prefer to make it a static member, unless it breaks the code.

See multithread discussion above.
The result could also mean that gcc cannot do "proper finagling"
(whatever that means...would you care to explain?)

An exception to the rule about distinct addresses occurs for
base objects. Put a zero-sized object (alone) in a base class
and it need not have any padding. Thus it contributes nothing
to the size of the class. Our library does this trick, as do
others.

P.J. Plauger
Dinkumware, Ltd.
http://www.dinkumware.com
 

Ask a Question

Want to reply to this thread or ask your own question?

You'll need to choose a username for the site, which only take a couple of moments. After that, you can post your question and our members will help you out.

Ask a Question

Members online

Forum statistics

Threads
474,166
Messages
2,570,907
Members
47,446
Latest member
Pycoder

Latest Threads

Top