Home > Back-end >  Allocator and STL containers
Allocator and STL containers

Time:12-13

I'm trying to reproduce the std::vector container, and I saw that in one of the vector constructor prototype we can give an allocator.

explicit vector (size_type n, const value_type& val = value_type(),const allocator_type& alloc = allocator_type());

So two questions came to my mind first, why are we able to give an allocator knowing that we already have the allocator_type member type ? second, what happen if I give a different allocator to the constructor ?

So I've tried this code.

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

int main()
{
    std::allocator<bool> alloc;

    std::vector< std::string > ve(10, "hello", alloc);
    
    for (int i = 0; i < 10; i  )
        std::cout << ve[i] << " | ";
    std::cout << "\n";

    return 0;
}

Giving a bool allocator to construct a std::string vector works fine and valgrind doesn't complain about it.

So I'm wondering if the bool allocator gived to the constructor is really used, and if he's not what's the point to be able to give an allocator that can be different from the allocator_type to the constructor ?

Thanks for reading me.

CodePudding user response:

The code that you have posted will work with some standard library implementations, and fail to compile with others.

Libstdc , for example, will take the allocator you pass, and immediately create a new allocator (of the correct type) using rebind.

Libc , on the other hand, will reject the code with a static_assert message.

CodePudding user response:

why are we able to give an allocator knowing that we already have the allocator_type member type ?

Same reason any type is different from an instance of that type: state.

It is possible to write stateful allocators, where the specific allocator object passed to a particular container affects how it works (for example, which memory pool it allocates from).

what happen if I give a different allocator to the constructor ?

For your particular case, even before C 20, std::allocator has a non-explicit converting constructor which will construct the correct std::allocator<std::string> from the argument you passed.

Obviously this only works when the allocator type parameter is either std::allocator, or some user-defined allocator with a similar converting constructor.


For clarity, there is also a rebind mechanism specifically to help with changing an allocator<T> parameter to a related allocator<U>.

This is needed for node-based containers like

std::map<Key, Value, Compare, Allocator=std:allocator<std::pair<const Key, Value>>>

because the map doesn't really allocate pair<const Key,Value>, but some private internal type like map<Key,Value>::__node_impl. Obviously the client can't be expected to pass an allocator of the correct type, because it doesn't know the correct type.

Before C 17 this was a member template, and now it uses std::allocator_traits instead. It doesn't participate at all in your example code, though, because the allocator constructor argument still needs to be the same type as the type parameter, even if that isn't the type eventually used.

CodePudding user response:

The allocator_type member (which is equal to the corresponding template parameter of the std::vector class template) determines the type of the allocator used by the vector. Its value_type member must be equal to the value_type of the vector, otherwise there is no guarantee that the vector will work correctly. But in your example allocator_type is std::allocator<std::string> (from the default template argument), matching the value_type which is std::string.

But that determines only the type of the allocator, not its value. An allocator is allowed to have state, so you can't always use it just knowing its type. (Although that is sufficient in the case of std::allocator, which is stateless.)

The parameter to the constructor takes the value for the allocator to use by the vector (and is defaulted to be default-constructed). It is of the exact same type allocator_type. It is not a different type. If you pass it a different type, then either it will fail to compile or it will implicitly convert to allocator_type. Any specialization of std::allocator can be implicitly converted to any other, which is why it doesn't matter what specialization of std::allocator you pass to the constructor. (For allocators other than std::allocator the conversion only needs to be possible explicitly, not necessarily implicitly.) The constructor is not templated on the allocator type. It has a parameter of type allocator_type exactly.

  • Related