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_type
is 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 forn
objects 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::vector
page: " the behavior is undefined ifAllocator::value_type
is not the same asT
.",T
being 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