what does (template) rebind do?
The _Alloc
template is used to obtain objects of some type. The container may have an internal need to allocate objects of a different type. For example, when you have a std::list<T, A>
, the allocator A
is meant to allocate objects of type T
but the std::list<T, A>
actually needs to allocate objects of some node type. Calling the node type _Ty
, the std::list<T, A>
needs to get hold of an allocator for _Ty
objects which is using the allocation mechanism provided by A
. Using
typename _A::template rebind<_Ty>::other
specifies the corresponding type. Now, there are a few syntactic annoyances in this declaration:
- Since
rebind
is a member template of_A
and_A
is a template argument, therebind
becomes a dependent name. To indicate that a dependent name is a template, it needs to be prefixed bytemplate
. Without thetemplate
keyword the<
would be considered to be the less-than operator. - The name
other
also depends on a template argument, i.e., it is also a dependent name. To indicate that a dependent name is a type, thetypename
keyword is needed.
Why are allocators to containers passed as template parameters?
Because this would work with only one allocator - standard one. But what if you want to allocate memory differently? For example, you might want to use shared memory, or file-backed memory, or anything else.
This is the whole point of having allocators - to allow user to customize the way memory is going to be allocated and freed.
A query regarding allocator rebind
The standard library always accesses Allocators through the std::allocator_traits
template. This template provides a default definition of rebind
if it conforms to the form Alloc<U, Args>
rebind_alloc<T>
Alloc::rebind<T>::other
if present, otherwiseAlloc<T, Args>
if thisAlloc
isAlloc<U, Args>
- cppreference.com : std::allocator_traits
Why do container allocators need to specify the type they're allocating?
It's a good question, and your suggestion is one possible alternative to the standard scheme. Another would have been to use template template parameters:
template<typename T>
class AnAllocator
{ ... };
template<typename T, template <typename> class Alloc = std::allocator>
class Vector
{
typedef Alloc<T> allocator_type;
...
};
Vector<int, AnAllocator> v;
The allocator interface was designed before template template parameters were part of the language, so that wasn't an option.
There are lots of things that would be done differently if the allocator API were to be designed today, unfortunately we're stuck with the one we have (and the continuing complexity caused by trying to extend it in semi-compatible ways).
How to avoid rebind in allocator T, N c++17
Only std::allocator
's rebind
member template is deprecated. If you are using your own class, you can still define rebind
.
Do it through std::allocator_traits
, like:
using AllocatorForU = std::allocator_traits<AllocatorForT>::template rebind_alloc<U>;
The default for rebind_alloc
for AllocatorTemplate<T, OtherTypes...>
is AllocatorTemplate<U, OtherTypes...>
, which works for std::allocator
, which is why std::allocator<T>::rebind
is deprecated. You have to define it for your class since it has a non-type template parameter.
C++ custom allocator size argument as template parameter throws compiler error
To get an allocator for some type U
from an allocator for type T
, member alias template std::allocator_traits::rebind_alloc<U>
is used [allocator.traits.types]:
Alloc::rebind<T>::other
ifAlloc::rebind<T>::other
is valid and
denotes a type; otherwise,Alloc<T, Args>
ifAlloc
is a class template instantiation of the formAlloc<U, Args>
, whereArgs
is zero or more type arguments; otherwise, the instantiation ofrebind_alloc
is ill-formed.
Note that Args
are type template parameters. In your allocators there is no rebind
. In the first case Alloc<U>
is used, Args
is empty. But in the second case, the second template parameter is a non-type one, it cannot be matched by Args
.
You need to manually add rebind
member struct:
template<typename T, std::size_t sz>
class StaticAllocator {
// ...
template<class U>
struct rebind {
using other = StaticAllocator<U, sz>;
};
};
Also note that your allocator is broken for some general type S
. First, buf
will be initialized by default constructing sz
objects S
. Then, it will be overwritten by newly constructing S
s at the same location before destructing the existing ones. The similar thing happens upon reallocation. This can lead to undefined behaviour. See this and this questions for some details.
The following solution was proposed in the now-deleted answer: inherit from std::allocator<T>
:
template<typename T, std::size_t sz> class StaticAllocator :
public std::allocator<T> {
// ...
};
The code compiles and runs, but... StaticAllocator::allocate
and StaticAllocator::deallocate
are not called (at least in libstdc++). The reason is that internally std::vector
always uses rebind
to get the allocator type:
using Tp_alloc_type =
typename gnu_cxx::alloc_traits<Alloc>::template rebind<Tp>::other;
rebind
is inherited from std::allocator<T>
and it returns std::allocator
instead of StaticAllocator
. That's why you still have to provide your own rebind
in StaticAllocator
.
Purpose of rebind in the following container
It is not a data member. It is a template member, that allows code (which is probably itself a template) to get a different container type with a similar allocator.
e.g. applying a function to each element to get a new container of the results
template <typename Container, typename Function, typename Result = typename Container::template Rebind<std::invoke_result_t<Function, typename Container::const_reference>>::Other>
Result transform(const Container & container, Function function)
{
Result result(container.get_allocator());
for (auto & element : container) { result.push_back(function(element); }
return result;
}
Can I use allocator specified for some type to allocate objects of another type in C++?
std::allocator
has a member type rebind
for exactly that purpose:
std::allocator<Node> alloc;
std::allocator<Node>::rebind<char>::other char_alloc;
char * mem = char_alloc.allocate(string_len);
In allocator-aware STL classes, why are the allocators not template template arguments?
This would introduce a requirement that the allocator type be a class template with exactly one template argument, specialized with the container's value_type
. Your proposal would eliminate
template<typename T, unsigned int PoolNumber = 0>
class my_allocator;
as a valid allocator.
At the same time, I can simply use the typedef
I already have for my allocator type, and don't need to take it apart or repeat its template name:
template<typename T> class my_allocator;
typedef my_allocator<int> int_allocator;
std::list<int, int_allocator> ... // valid currently, difficult to express with your proposal
Related Topics
When and How to Use Exception Handling
Stack-Buffer Based Stl Allocator
What's Faster, Iterating an Stl Vector with Vector::Iterator or with At()
Search for a Struct Item in a Vector by Member Data
Practical Use of Zero-Length Bitfields
Allocating More Memory Than There Exists Using Malloc
Stdafx + Header File - Order of Inclusion in Mfc Application
Template Instantiation Details of Gcc and Ms Compilers
Opengl - Mask with Multiple Textures
How to Avoid the Diamond of Death When Using Multiple Inheritance
How to Use Createfile, But Force the Handle into a Std::Ofstream
Isdigit(C) - a Char or Int Type
Why Does Int*[] Decay into Int** But Not Int[][]
C++ Compare Char Array with String
Is Storing an Invalid Pointer Automatically Undefined Behavior