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.
Table 98 -- Allocator-aware container requirements says in its first row:
Requires:
allocator_type::value_typeis the same asX::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.
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 fornobjects of typeT, 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::vectorpage: " the behavior is undefined ifAllocator::value_typeis not the same asT.",Tbeing the value type of the vector.
Ie. in your case, the behaviour is undefined.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With