Can be used separately from each other?
The template parameter just supplies the type. You still need an instance. It's not separable.
It's like having a function template<typename Type> f(Type instance);
and asking what is the difference between Type
and instance
, can they be used separately and what are the advantages of either. It does not make much sense if you do understand what is a template, type and an instance/object.
(for the sake of simplicity it's c++11)
Here you have type template for vector
:
template<
class T,
class Allocator = std::allocator<T>
> class vector;
And here is the default constructor:
explicit vector( const Allocator& alloc = Allocator() );
There always is an instance of Allocator
provided as alloc
parameter. All other invocation are similar in this regard. By default it is default constructed new Allocator
object. So, semantically, whenever you do not use invocation of vector specifying allocator
parameter, you do create new Allocator
object (which in default case most probably does nothing, but the logical flow of the program is as described).
You cannot pass something that would not fit Allocator
because you would get type-mismatch, or precisely in this case a substitution failure.
One pretty non-standard you could do without touching the definition of vector
is to define DerivedAllocator
which derives from Allocator
instantiate it and pass as an argument. So for example:
vector<T> v( DerivedAllocator<T>() );
But I am not able to come up with a use-case for such construction on the top of my head. There is a good use-case, see the addendum below.
What is the Allocator
template parameter useful for?
In some system you have more than one type of memory, so it might be useful to provide separate allocators (presicely separate allocator types). E.g: SRamAllocator
, RamAllocator
, etc.
This is quite common in embedded systems. I know that somewhere there there is a memory model in implementation which actually does not free, when you free it it's a lost chunk. It's essentially a moving pointer. The rationale is that it's extremely fast because it does not have any logic to trace blocks of "holes" caused by free
ing. You wouldn't want to use it scenarios with heavy new
/delete
patterns.
What is the allocator
constructor parameter useful for?
It makes sense in case of stateful allocators. Imagine you want to have two storages of the same type. E.g. to track some memory usage, or whatever reason you come with to have more than one logical "memory banks". You may want to create an allocator for each thread in your program, so it's easier to maintain correct CPU/memory affinity.
When you create a new object, you need to tell which of the allocators instances should take care of it.
You could technically implement everything just using different type for each instance, but that would strip down the usability of possible run-time dynamism.
NOTE: Default allocator and pre-c++11 custom allocators are disallowed to have a state, so they basically that to be implemented in a fully static way. It actually does not matter instance of Allocator you use. That is why the default Allocator()
works.
So, theoretically one would no need then to instantiate them, and could work with just type and a static interface... if the standard said so. But it was deliberately not made this way to allow allocator types with an internal state (this sentence is a personal opinion).
IMPORTANT ADDENDUM: I've missed one important perk of c'tor parameter allocator, which is quite possibly it's raison d'être. Polymorphic allocators. Is described in detail here: polymorphic_allocator: when and why should I use it?
Basically, using different Allocator
type would change the whole type of the object, so one end's up with basically the same object which differ only by allocator. This is under certain circumstances highly undesirable. To avoid it, one can write a polymorphic allocators and use base allocator in the type, and concrete implementations as the runtime parameters. Therefore, one can have object of exactly the same type using different storage engines. So using parameter has some overhead, but it reduces status of the allocator from being iron branded onto the type, to more of an implementational detail.
int n = 5
, where you haveint
on the left and5
on the right. It's not either/or, you need both type and value. – Fourlegged