3

I am just learning about C++ allocators, and I am trying to understand the purpose of the struct rebind in every allocator. For example, in this program:

#include <memory>
#include <iostream>
#include <algorithm>
#include <vector>

using namespace std;

typedef vector<int>::allocator_type IntAlloc;
int main( ) 
{
   IntAlloc v1Iter;
   vector<int> v1;

   //************What's going on here?********
   IntAlloc::rebind<char>::other::pointer pszC =
      IntAlloc::rebind<char>::other(v1.get_allocator()).allocate(1, (void *)0);

   int * pInt = v1Iter.allocate(10);
}

I am trying to understand what the key line is doing. Is it modifying the typedef IntAlloc to now server as an allocator for a char vector? Even if I have guessed correctly, I am not sure I can break it down. Here's my guess:

IntAlloc::rebind<char> //accessing the struct rebind

`::other` //accessing data member other inside rebind

(v1.get_allocator()) //**isn't this just retrieving IntAlloc in its original form?** 
///What is this for?

.allocate(1, (void *)0); //**this is allocating something? What do these parameters mean?**
mpromonet
  • 11,326
  • 43
  • 62
  • 91
helloB
  • 3,472
  • 10
  • 40
  • 87
  • Other is a type: `typedef vector::allocator_type::rebind::other char_allocator;` –  Jul 30 '15 at 17:08
  • @DieterLücking so it looks like this typedef-ed type is then getting initialized with what follows after other(...? – helloB Jul 30 '15 at 17:11
  • Looks like a Microsoft specific extension, can't find any standard definitions: http://en.cppreference.com/mwiki/index.php?title=Special%3ASearch&search=rebind – πάντα ῥεῖ Jul 30 '15 at 17:11
  • 1
    The example is constructing an `allocator` from an `allocator` using [constructor (3)](http://en.cppreference.com/w/cpp/memory/allocator/allocator). The `allocate` call is allocating 1 char, with a `nullptr` *hint*. It might be helpful to think of `std::list` instead of `vector` to see why `rebind` is useful. Even if you have a `list`, the standard library implementation needs to allocate some private *Node* type, not `int`s, because the Nodes also need to contain pointers to previous/next nodes. So `list` obtains the necessary `allocator` via `rebind`. – Praetorian Jul 30 '15 at 17:30
  • And if you're going to spend the time learning allocators, you'll probably be better off learning the C++11 additions. It adds support for stateful allocators, and everything goes through an abstraction layer - `std::allocator_traits`. @πάνταῥεῖ It's not MS specific, take a look inside `std::allocator`. – Praetorian Jul 30 '15 at 17:33
  • @Praetorian Not from [here](http://en.cppreference.com/w/cpp/memory/allocator) at least :-( ... – πάντα ῥεῖ Jul 30 '15 at 17:43
  • @πάνταῥεῖ *Ctrl + F*, type `rebind`, hit *Enter* :) (Adjust as necessary for keyboard layouts and such) – Praetorian Jul 30 '15 at 17:52
  • @Praetorian Sorry, yes. I seriously need glasses! Working from that tablet makes me disabled anyhow ;-) ... – πάντα ῥεῖ Jul 30 '15 at 17:53

2 Answers2

0

This is all pre C++11 Allocator work unfortunately, they dramatically simplified things with the 2011 standard.

struct rebind is essentially a way to get around the fact that there aren't templated typedefs in the language, which are needed by Containers that don't allocate the same type internally as the allocator that you pass them. They "rebind" the allocator and get back a new allocator that can allocate a different type (for example, 'std::set's allocate some 'Node' type that has pointers to other Nodes for a tree structure, in addition to an object of the type your allocator allocates). This mechanism hides the internal structure, but sure isn't pretty.

So for example, you might have an Allocator<int>, but to make a tree structure, std::set needs to allocate from an object of type Allocator<std::_Node<int>> or something like that. The rebind mechanism allows that.

(v1.get_allocator()) //isn't this just retrieving IntAlloc in its original form?

This is getting the allocator object from v1, which is of type vector<int>::allocator_type, so more or less yes.

IntAlloc::rebind<char>::other(v1.get_allocator()) is getting a char allocator, and initializing it to use the allocator from the vector<int>, v1.

R2-Dequeue
  • 638
  • 1
  • 5
  • 15
  • thanks a lot for this explanation. I am trying to learn C++ allocators, but I haven't been able to find a comprehensive example, and I'm a beginner, so I find I do not learn much just trying to plow through the documentation on my own. Any suggestions for what to read? – helloB Jul 30 '15 at 17:49
  • 1
    @helloB Glad you asked! I do: [CppCon 2014: Stephan Lavavej "STL Features And Implementation Techniques"](http://www.youtube.com/watch?v=dTeKf5Oek2c&t=26m25s) [CppCon2014: Alisdair Meredith "Making Allocators Work, Part I"](https://www.youtube.com/watch?v=YkiYOP3d64E) [CppCon 2014: Alisdair Meredith "Making Allocators Work, Part II"](https://www.youtube.com/watch?v=Q5kyiFevMJQ) You can get a recent draft standard of C++14 [here](http://www.open-std.org/jtc1/sc22/wg21/docs/papers/2014/n4296.pdf) and start with section 17.6.3.5 Allocator requirements [allocator.requirements]. – R2-Dequeue Jul 30 '15 at 18:20
0

These lines

IntAlloc::rebind<char>::other::pointer pszC =
      IntAlloc::rebind<char>::other(v1.get_allocator()).allocate(1, (void *)0);

will use the same allocator used by v1 (a std::vector<int>) to allocate one char (if possible near the address 0x0) But the allocator used by an std::vector actually only knows how to allocate and build an int, not a char, so you have to "rebind" the allocator's type to a char allocator (then you use the static method allocate, and implicitly convert [construct a char allocator out of] your int allocator v1.get_allocator() to a char allocator)

That way, if your allocator was actually providing blocks of memory out of an already allocated pool of memory (for instance), you can reuse the same pool for different types.

See this and this for more details.

Caninonos
  • 1,214
  • 7
  • 12