max_size()
for an allocatorSection: 16.4.4.6 [allocator.requirements] Status: New Submitter: Jon Cohen Opened: 2017-12-06 Last modified: 2024-01-29
Priority: 3
View other active issues in [allocator.requirements].
View all other issues in [allocator.requirements].
View all issues with New status.
Discussion:
Table 31 in the C++17 standard specifies X::max_size()
(where X
is an allocator type) as "The largest value
that can meaningfully be passed to X::allocate()
". Noticeably missing is the statement "Throws: Nothing".
vector::max_size()
and allocator_traits::max_size()
are
both marked noexcept
. We must then interpret max_size()
as being allowed to sometimes call
std::terminate
, or else {vector, allocator_traits, ...}::max_size()
must be allowed to directly calculate
numeric_limits<size_type>::max() / sizeof(value_type)
instead of querying the allocator, even if
Alloc::max_size()
exists. This seems like a bug in the wording for the requirements of max_size()
in an
allocator type. I think an issue should be opened on this subject to add Throws: Nothing or similar to the requirements
of max_size()
for an allocator.
As an example consider writing up a framework to test the exception-safety of types in a given framework, since they were all
written in an exception-free environment. One of the types in the framework is an allocator which, in a controlled way,
can throw an exception at any point where it is allowed by the standard. It's important that the test framework be as pedantic
as possible, so the allocator type throws on max_size()
, since it is currently allowed to by the standard. When a
reasonable vector
implementation (at least those in libstdc++ and msvc) is, for example, asked to construct a
vector
from an initializer_list
, it will call allocator_traits<Alloc>::max_size()
, which will
terminate the program because the exception thrown in Alloc::max_size()
propagated through the noexcept
traits function. Although this is conformant behavior, I think it's a bug in the standard that a function as benign as
max_size()
can terminate the program in this manner, and I think the fix is that a conformant allocator should be
required to supply a non-throwing max_size()
member function.
Daniel:
This problem was shortly discussed during review of LWG 2162 (see comment 2012-08-05). At that time
the more drastic but also more consistent requirement that an allocator's max_size
function shall not throw
exceptions has not been added. IMO this position should be reconsidered to follow the spirit of the new issue LWG
3044.
[2018-01; Priority set to 3 after mailing list discussion]
[2018-08-21, Jonathan comments and provides wording]
The phrase "the largest value that can meaningfully be passed to X::allocate()
" is meaningless. Is it a
requirement on the caller, so that larger values must not be passed? Or a hint from the allocator implementor that larger
values will produce a bad_alloc
exception? Can the return value change dynamically, based on the free memory
available to the allocator?! — LWG 197 says it can't change.
const
object
(so allocator_traits
will not use the allocator's max_size()
if it's non-const
, although that was
unclear before DR 2284). In addition to adding "Throws: nothing" we should ensure it's callable
on const
lvalues, and clarify what "meaningfully" means and who is supposed to care about it. My proposed
resolution doesn't achieve all of this, but is a start.
Previous resolution [SUPERSEDED]:
This wording is relative to N4762.
Change 16.4.4.6 [allocator.requirements], Table 32 — "Descriptive variable definitions", as indicated:
Table 32 — Descriptive variable definitions Variable Definition T, U, C
any cv-unqualified object type (3.9) …
a, a1, a2
lvalues of type X
a3
an lvalue of type const X
…
Change 16.4.4.6 [allocator.requirements], Table 33 — "Cpp17Allocator requirements", as indicated:
Table 33 — Cpp17Allocator requirements Expression Return type Assertion/note
pre-/post-conditionDefault …
a3.max_size()
X::size_type
the largest value that can
meaningfully be passed to
X::allocate()
.
[Note: Larger values might cause
an exception to be thrown. — end note]
Throws: Nothing.numeric_limits<size_type>::max()
/ sizeof(value_type)…
[2022-04-25; Daniel rebases wording on N4910]
Proposed resolution:
This wording is relative to N4910.
Change 16.4.4.6.1 [allocator.requirements.general] as indicated:
-2- In subclause 16.4.4.6 [allocator.requirements],
(2.1) — […]
[…]
(2.6) —
a
,a1
,a2
denote lvalues of typeX
,(?.?) —
a3
denotes an lvalue of typeconst X
,[…]
[…]
a3.max_size()-50- Result:
-51- Returns: The largest value that can meaningfully be passed toX::size_type
X::allocate()
. [Note: Larger values might cause an exception to be thrown. — end note] -?- Throws: Nothing. -52- Remarks: Default:numeric_limits<size_type>::max() / sizeof(value_type)