This page is a snapshot from the LWG issues list, see the Library Active Issues List for more information and the meaning of New status.

### 3044. Strange specification of `max_size()` for an allocator

Section: 16.4.4.6 [allocator.requirements] Status: New Submitter: Jon Cohen Opened: 2017-12-06 Last modified: 2022-04-25

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".

As an example of why this is an issue, note that `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.

As noted in the LWG 2162 comments, we don't currently guarantee it can be called on a `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.

> 1. 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`
>    > `…`

> 2. Change 16.4.4.6 [allocator.requirements], Table 33 — "Cpp17Allocator requirements", as indicated:

>    > Table 33 — Cpp17Allocator requirements
>    > Expression | Return type | Assertion/note pre-/post-condition | Default
>    > --- | --- | --- | ---
>    > `…`
>    > `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.

1. Change 16.4.4.6.1 [allocator.requirements.general] as indicated:

   > -2- In subclause 16.4.4.6 [allocator.requirements],

   > 1. (2.1) — […]

   > 2. […]

   > 3. (2.6) — `a`, `a1`, `a2` denote lvalues of type `X`,

   > 4. (?.?) — `a3` denotes an lvalue of type `const X`,

   > 5. […]

   > […]

   >     a3.max_size()

   > > -50- Result: `X::size_type`

   > > -51- Returns: 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.

   > > -52- Remarks: Default: `numeric_limits<size_type>::max() / sizeof(value_type)`