skypjack
skypjack

Reputation: 50540

Allocators: how are the standard containers expected to work internally?

As an example for this question, I'll use std::vector.
Its definition from the documentation follows:

template<class T, class Allocator = std::allocator<T>>
class vector;

As expected, if T is its type, the allocator should be biased towards T.
Anyway, the code below compiles with no errors (at least, using GCC) and runs:

#include<vector>
#include<memory>
#include<string>

struct S {
    int i;
    double d;
    std::string s;
};

int main() {
    std::allocator<int> alloc;
    std::vector<S, std::allocator<int>> v{alloc};
    v.push_back(S{});
}

Here, I'm creating a vector of S by using an allocator focused on int.

Is it legal code? Should I expect undefined behavior? What else?
I don't fully understand what the magic behind that and why the STL lets the users do that.
On the other side, things like rebind are mentioned only in the documentation of std::list and I don't know if they are meant also for such a case.

In other therms, if it works I'd like to know why it works (is it rebind behind that?), otherwise I'd like to know why it is allowed.

Upvotes: 4

Views: 145

Answers (2)

Marcus M&#252;ller
Marcus M&#252;ller

Reputation: 36346

It seems that it works, which kind of surprises me, but:

You're breaking a contract. allocator must fulfill the requirements for allocator<T>, and to cite cppreference, this includes:

a.allocate(n) | allocates storage suitable for n objects of type T, but does not construct them. May throw exceptions.

Which std::allocator<int> for S doesn't do, as sizeof(int)!= sizeof(S).

So,

I'd like to know why it is allowed.

It's not allowed. It's just nothing your compiler can detect.

@AlbertoM adds:

To add a quote from cppreference, in the std::vector page: " the behavior is undefined if Allocator::value_type is not the same as T.", T being the value type of the vector.

Ie. in your case, the behaviour is undefined.

Upvotes: 2

Howard Hinnant
Howard Hinnant

Reputation: 218750

Table 98 -- Allocator-aware container requirements says in its first row:

Requires: allocator_type::value_type is the same as X::value_type

Violation of a Requires clause by the client code results in undefined behavior.

gcc and VS do not detect this error. They allow the code to work by rebinding the allocator to the appropriate type. libc++ actively detects this error with a static_assert.

http://melpon.org/wandbox/permlink/LVYEHfVIGoyZsii8

All three implementations of vector are valid in this regard.

The rationale for the libc++ active detection of this error is to find your errors for you faster (and at compile time). It would be a bad thing if in a large program you had two vectors: vector<int, some_allocator<int>> and vector<int, some_allocator<long>> and the program logic assumed these were the same type.

Upvotes: 7

Related Questions