Take from here (which is quite old):
It's also important that the type used for the allocator template parameter and the type used as the element type in a standard container agree. For instance:
std::list<int, std::allocator<long> > // Wrong!
won't work.
Question
Is the above statement correct (or was it ever correct)? Any tests I have done seem to work fine no matter what I put for T
in std::allocator
. For example, std::vector<int, std::allocator<std::string>>
compiled and worked fine pushing back and erasing elements, etc.
(From what I understand std::allocator<std::string>::rebind<int>::other
is the magic that makes this work).
EDIT: In [containers.requirements.general], the Allocator-aware container requirements indicate that the allocator_type::value_type
is the same as Container::value_type
.
So it's ill formed to pass in an allocator type with a different value_type
, although at least one implementation simply uses allocator_traits<...>::rebind<value_type>
to get the correct allocator.
I'm adding an answer here to clarify the difference between ill-formed and undefined behavior.
[intro.compliance]/p1:
The set of diagnosable rules consists of all syntactic and semantic rules in this International Standard except for those rules containing an explicit notation that “no diagnostic is required” or which are described as resulting in “undefined behavior.”
[defns.ill.formed]:
program that is not well formed
[defns.well.formed]
C++ program constructed according to the syntax rules, diagnosable semantic rules, and the One Definition Rule (3.2).
In English: an ill-formed program shall have a diagnostic associated with it. Undefined behavior can do anything:
(all but the 4th routinely happen in practice)
Undefined behavior is very bad, and imho, the C and C++ standards apply that specification much too liberally.
Technically, violating a Requires clause results in undefined behavior.
[res.on.required]/p1:
Violation of the preconditions specified in a function’s Requires: paragraph results in undefined behavior unless the function’s Throws: paragraph specifies throwing an exception when the precondition is violated.
As noted by MSN, allocator_type::value_type
shall be the same as container::value_type
as stated in Table 99 -- Allocator-aware container requirements.
allocator_type A Requires: allocator_type::value_type
is the same as X::value_type.
(X
denotes an allocator-aware container class with a value_type
of T
using allocator of type A
)
So a violation such as:
std::list<int, std::allocator<long> >
is undefined behavior. So it:
Just very recently (within weeks of me writing this), libc++ (http://libcxx.llvm.org) has started diagnosing this undefined behavior with static_assert
so that you get the bad news asap.
We decided to go this direction, instead of allowing the behavior because the containers are not set up to allow conversions among closely related types. For example:
std::list<int, std::allocator<long>> list1;
std::list<int> list2 = list1; // is specified to not work
I.e. if you start treating list1
and list2
as equivalent types because the std::allocator
gets rebind
'd anyway, you're going to get disappointed down the road as you discover the two lists are really different types and not designed to interoperate anyway. So it is really best to get the bad news asap, instead of finding out 2 months, or 2 years later, when you try to use them as equivalent types.
Perhaps a future standard will treat list1
and list2
as equivalent types. It is mostly technically possible (std::is_same
would likely not work). But there are no proposals that I've heard of in this direction. This direction does not seem likely to me. And with static_assert
, the error is easily diagnosable. Instead I would like to see the standard move in the direction of making this code ill-formed instead of undefined. The hardest part in doing so would be word-smithing the standard, and not in the std::lib implementation.
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