Freestanding constexpr containers and constexpr exception types

Document number: P3295R1
Date: 2024-09-15
Reply-to: Ben Craig <ben dot craig at gmail dot com>
Audience: Library Evolution Working Group

Changes from previous revisions

R1

R0

First revision!

Introduction

This proposal helps enable P2996 reflection and more advanced metaprogramming for freestanding environments. Specifically, this allows std::vector, std::string, and std::allocator to be used in constant evaluated contexts in freestanding.

As a first approximation, this makes the vector and string facilities consteval in freestanding. Freestanding implementations that know more about their targets are permitted to provide the full constexpr facilities if they wish.

This paper is attempting to modify the minimum set of facilities possible to freestanding in order to enable reflection, plus some additional exception types that come along for "free".

Motivation and Scope

Freestanding applications are often unable to allocate memory or throw exceptions at runtime. No such restriction exists while building the application though.

Reflection in P2996R2 contains many consteval functions that return vector<meta::info>, such as members_of. Those facilities are valuable in freestanding and hosted alike.

This paper would "only" make vector and allocator consteval on freestanding if it reasonably could as a means to limit scope, but vector has functions that throw length_error and out_of_range. length_error and out_of_range are not in freestanding prior to this paper, so mentioning them renders the program ill-formed, even in a constant evaluation context. This paper therefore makes those exception types constexpr on hosted, and consteval on freestanding. Making these exception types constexpr is likely to be a benefit for long-term reflection error handling plans (e.g. P3068 Allowing exception throwing in constant-evaluation).

Similarly, length_error and out_of_range mention string, which is also not yet in freestanding. Therefore the scope expands to string. Fortunately, char_traits and most of string_view are already freestanding.

All the <stdexcept> exceptions are receiving the same consteval treatment, since the classes are largely identical other than the class names.

The freestanding-deleted methods of string_view will need to be made consteval on freestanding in order to support strings use cases.

Hosted constexpr additions:

freestanding-consteval additions:

Design Considerations

Freestanding all the constexpr things?

In P2268 Freestanding Roadmap, a strategy is discussed of making everything that is constexpr in the standard library available at compile time via consteval, and those facilities that are also suitable at runtime to be marked constexpr and // freestanding. That is still the desired goal. Reaching that goal requires a lot of research and testing. There are likely other, non-constexpr facilities that the constexpr facilities rely on that would need to be audited as well.

Destructors cannot currently be marked as consteval. virtual functions that override constexpr methods cannot currently be marked as consteval. Those restrictions also prevent us from blanket consteval'ing all the constexpr facilities in the standard library.

This paper has chosen to prioritize the reflection subset of that work, rather than block progress on freestanding reflection.

Performance regression when porting from freestanding to hosted

consteval functions are never evaluated during runtime, but constexpr functions are sometime evaluated during runtime. A freestanding library using a consteval facility may end up "downgrading" some code from compile time to runtime during a port to a hosted environment due to the change to constexpr. This can result in worse performance due to the runtime function calls and allocations that were previously compile time function calls and allocations.

In this unoptimized example, we get runtime calls to an allocating constructor and deallocating destructor in the constexpr version compared to the consteval version. For this example, the difference disappears when optimizations are enabled, but that won't always be the case.

Non-transient constexpr allocations

There is a desire from some in the committee to allow constant evaluated vectors and strings persist into runtime. This is referred to as non-transient constexpr allocations. This paper doesn't propose such facilities, but the paper is trying to avoid causing issues for those future papers.

This paper is leaving non-throwing const methods as constexpr, rather than marking them as freestanding-consteval. This should make it easier to observe the values in containers with non-transient allocations possible in the future. This will allow us to avoid allocations and exceptions at runtime, while still allowing the constant compile time object to be observed.

One unfortunate aspect of this decision is that it makes operator[], front(), and back() as potentially callable at runtime, even though the only possible runtime containers are empty. LEWG should discuss whether we should prioritize future-proofing the containers or mistake-proofing them. For now, the paper has chosen to future-proof.

Freestanding operator delete and default constructed containers

Even when marked as consteval, a default constructed std::string or std::vector can persist into runtime. The destructor for such an object can be executed at runtime. That destructor will run operator delete on a nullptr.

Freestanding implementations may not have a heap [new.delete.general]#2. Conforming implementations aren't required to have a useable operator new, but they are required to have an operator delete that can handle a nullptr. That means that it is acceptable (though unfortunate) that an operator delete "escapes" into runtime. In practice, I don't think this will be a big problem, as there isn't much that can be done with a default constructed runtime container where all the mutating operations are consteval.

Ideally, this scenario would be ill-formed. The author is working on a paper to permit consteval destructors, which would improve this situation. consteval destructors aren't a requirement for this paper though.

Allocator requirements

Is std::allocator still an allocator if it can only allocate and deallocate at compile time?

In practice, this seems to work fine in code. It's challenging to specify this with the system of named requirements in combination with the layers of indirection imposed with allocator_traits.

This paper weakens the requirements for allocators, but only on freestanding implementations.

constexpr logic_errors

Philosophy

Various methods (vector::at, vector::reserve, string::compare, etc) throw exceptions in the logic_error hierarchy. For short term freestanding purposes, we could choose not to add the logic_error hierarchy, and instead have implementations #if that code out, and put something else in that spot that would cause the code to fail constant evaluation. However, there are proposals to permit throws during constant evaluation (P3068 Allowing exception throwing in constant-evaluation), and there is a desire to use this facility as part of post-C++26 P2996 reflection. So rather than have implementers add a hack only to remove it in the near future, we'll add the logic_errors we need to consteval freestanding, and mark them as constexpr in hosted.

There are some other reasons to make the logic_error hierarchy constexpr. There's the general "constexpr all the things" motivation. Some developers are also interested in having variant and expected objects with logic_error alternatives as a way to manage errors in constexpr contexts.

We could also choose to make the entirety of <stdexcept> consteval in freestanding. The different classes are mostly identical in terms of functionality.

Technical challenges

There are some reasons why making logic_error constexpr could be challenging.

The libc++ implementation of logic_error includes a reference counted string. The reference counting currently uses atomic operations, though it doesn't use std::atomic specifically. That implementation would need to conditionally use non-atomic operations during constant evaluation.

The libstdc++ implementation uses a copy-on-write (COW) string. Portions of the implementation of the logic_error hierarchy are in the library rather than headers. One of the COW string implementations use atomic operations for the reference counting implementation, but not specifically std::atomic

MSSTL appears to perform a deep copy of the strings in logic_error. It does not appear to involve atomics. Some of its implementation appears to be in the library, and not in headers.

P3037R1 constexpr std::shared_ptr also discusses reference counting in constexpr. SG7 Compile-time programming was fine with reference counting at compile time in the Tokyo 2024 meeting.

Experience

This proposal has been implemented in the Microsoft STL implementation, targeting the Microsoft Windows kernel. The binaries were compiled with clang-cl, as Microsoft's cl doesn't yet implement P2564: consteval needs to propagate up. consteval propagating up was necessary for compressed pair implementations. Some changes had to be made to vcruntime to move some std::exception code inline.

The libc++ test suite was used to verify the changes. I modified the tests to verify the compile-time paths (if it wasn't doing so already), and remove the run-time paths. I would expect production implementations to do this with an `#ifdef` to remove the run-time paths when testing the freestanding implementation.

I will note that the Microsoft Windows kernel implementation does not support C++ exceptions. As a result, the tests did not exercise any throwing paths.

Merge Conflicts

Exceptions

P3068 "Allowing exception throwing in constant-evaluation" and P3378 "constexpr all the exception things" each touch exception types that this paper also touches. Among other things, those papers mark various exception types as constexpr. There isn't any design conflict here though. Whichever paper makes it through LWG first can mark the various exceptions as constexpr.

Allocators

P3160R2 "An Allocator-Aware inplace_vector" needs to make allocators freestanding, since inplace_vector is already freestanding.

inplace_vector doesn't directly need the allocate and deallocate methods in the allocator. This paper needs those methods, but only during constant evaluation.

For allocator wording ([allocator.requirements.general] and [allocator.traits.memgers]), this paper's wording matches draft versions of P3160. This paper's freestanding-consteval changes in [default.allocator] are likely to supercede the freestanding-deleted changes from P3160.

Wording

This paper's wording is based on the current working draft N4988.

Changes in [functions.within.classes]

Instructions to the editor:

Move [functions.within.classes] below [freestanding.item]. This is so we can use the "freestanding consteval classes" term after it is defined. Then modify [functions.within.classes] as follows:

For the sake of exposition, [support] through [exec] and [depr] do not describe copy/move constructors, assignment operators, or (non-virtual) destructors with the same apparent semantics as those that can be generated by default ([class.copy.ctor], [class.copy.assign], [class.dtor]).
It is unspecified whether the implementation provides explicit definitions for such member function signatures, or for virtual destructors that can be generated by default.
On freestanding implementations, it is implementation defined whether the copy/move constructors and assignment operators of freestanding consteval classes are immediate functions or constexpr functions.

Changes in [freestanding.item]

Instructions to the editor:
Please make the following edits to [freestanding.item]:
Function declarations and function template declarations followed by a comment that include freestanding-deleted are freestanding deleted functions.
On freestanding implementations, it is implementation-defined whether each entity introduced by a freestanding deleted function is a deleted function ([dcl.fct.def.delete]) or whether the requirements are the same as the corresponding requirements for a hosted implementation.
[Note 1: 
Deleted definitions reduce the chance of overload resolution silently changing when migrating from a freestanding implementation to a hosted implementation.
— end note]
[Example 1: double abs(double j); // freestanding-deleted — end example]
Function declarations and function template declarations followed by a comment that includes freestanding-consteval are freestanding consteval functions.
On freestanding implementations, it is implementation-defined whether each entity introduced by a freestanding consteval function is an immediate function ([expr.const]) or whether the requirements are the same as the corresponding requirements for a hosted implementation.
Class declarations and class template declarations followed by a comment that includes freestanding-consteval are freestanding consteval classes.
A declaration in a synopsis is a freestanding item if
  • it is followed by a comment that includes freestanding,
  • it is followed by a comment that includes freestanding-deleted, or
  • it is followed by a comment that includes freestanding-consteval, or
  • the header synopsis begins with a comment that includes freestanding and the declaration is not followed by a comment that includes hosted.
    [Note 2: 
    Declarations followed by hosted in freestanding headers are not freestanding items.
    As a result, looking up the name of such functions can vary between hosted and freestanding implementations.
    — end note]
[Example 2: // all freestanding namespace std { — end example]

Changes in [compliance]

Drafting note: <string> and <memory> are already freestanding headers.
Add new rows to the "C++ headers for freestanding implementations" table:
SubclauseHeader(s)
[…] […] […]
?.? [std.exceptions] Exception classes <stdexcept>
[…] […] […]
?.? [vector.syn] Header <vector> synopsis <vector>
[…] […] […]

Changes in [version.syn]

Instructions to the editor:
Add the following macros to [version.syn]:
#define __cpp_lib_constexpr_exception 20XXXXL // freestanding, also in <exception>, <stdexcept>
#define __cpp_lib_freestanding_consteval_allocator 20XXXXL // freestanding, also in <memory>
#define __cpp_lib_freestanding_consteval_string 20XXXXL // freestanding, also in <string>
#define __cpp_lib_freestanding_stdexcept 20XXXXL // freestanding, also in <stdexcept>
#define __cpp_lib_freestanding_vector 20XXXXL // freestanding, also in <vector>

Changes in [exception]

Instructions to the editor:
Please make the following edits to [exception]:
namespace std {
  class exception {
  public:
    constexpr exception() noexcept;
    constexpr exception(const exception&) noexcept;
    constexpr exception& operator=(const exception&) noexcept;
    constexpr virtual ~exception();
    constexpr virtual const char* what() const noexcept;
  };
}
/* ... */
constexpr exception(const exception& rhs) noexcept;
constexpr exception& operator=(const exception& rhs) noexcept;

/* ... */
constexpr virtual ~exception();

/* ... */
constexpr virtual const char* what() const noexcept;

Changes in [stdexcept.syn]

Instructions to the editor:
Please make the following edits to [stdexcept.syn]:
namespace std {
  class logic_error; // freestanding-consteval
    class domain_error; // freestanding-consteval
    class invalid_argument; // freestanding-consteval
    class length_error; // freestanding-consteval
    class out_of_range; // freestanding-consteval
  class runtime_error; // freestanding-consteval
    class range_error; // freestanding-consteval
    class overflow_error; // freestanding-consteval
    class underflow_error; // freestanding-consteval
}

Changes in [logic.error]

Instructions to the editor:
Please make the following edits to [logic.error]:
namespace std {
  class logic_error : public exception {
  public:
    constexpr explicit logic_error(const string& what_arg); // freestanding-consteval
    constexpr explicit logic_error(const char* what_arg); // freestanding-consteval
  };
}
/*...*/
constexpr logic_error(const string& what_arg);
/*...*/
constexpr logic_error(const char* what_arg);

Changes in [domain.error]

Instructions to the editor:
Please make the following edits to [domain.error]:
namespace std {
  class domain_error : public logic_error {
  public:
    constexpr explicit domain_error(const string& what_arg); // freestanding-consteval
    constexpr explicit domain_error(const char* what_arg); // freestanding-consteval
  };
}
/*...*/
constexpr domain_error(const string& what_arg);
/*...*/
constexpr domain_error(const char* what_arg);

Changes in [invalid.argument]

Instructions to the editor:
Please make the following edits to [invalid.argument]:
namespace std {
  class invalid_argument : public logic_error {
  public:
    constexpr explicit invalid_argument(const string& what_arg); // freestanding-consteval
    constexpr explicit invalid_argument(const char* what_arg); // freestanding-consteval
  };
}
/*...*/
constexpr invalid_argument(const string& what_arg);
/*...*/
constexpr invalid_argument(const char* what_arg);

Changes in [length.error]

Instructions to the editor:
Please make the following edits to [length.error]:
namespace std {
  class length_error : public logic_error {
  public:
    constexpr explicit length_error(const string& what_arg); // freestanding-consteval
    constexpr explicit length_error(const char* what_arg); // freestanding-consteval
  };
}
/*...*/
constexpr length_error(const string& what_arg);
/*...*/
constexpr length_error(const char* what_arg);

Changes in [out.of.range]

Instructions to the editor:
Please make the following edits to [out.of.range]:
namespace std {
  class out_of_range : public logic_error {
  public:
    constexpr explicit out_of_range(const string& what_arg); // freestanding-consteval
    constexpr explicit out_of_range(const char* what_arg); // freestanding-consteval
  };
}
/*...*/
constexpr out_of_range(const string& what_arg);
/*...*/
constexpr out_of_range(const char* what_arg);

Changes in [runtime.error]

Instructions to the editor:
Please make the following edits to [runtime.error]:
namespace std {
  class runtime_error : public logic_error {
  public:
    constexpr explicit runtime_error(const string& what_arg); // freestanding-consteval
    constexpr explicit runtime_error(const char* what_arg); // freestanding-consteval
  };
}
/*...*/
constexpr runtime_error(const string& what_arg);
/*...*/
constexpr runtime_error(const char* what_arg);

Changes in [range.error]

Instructions to the editor:
Please make the following edits to [range.error]:
namespace std {
  class range_error : public logic_error {
  public:
    constexpr explicit range_error(const string& what_arg); // freestanding-consteval
    constexpr explicit range_error(const char* what_arg); // freestanding-consteval
  };
}
/*...*/
constexpr range_error(const string& what_arg);
/*...*/
constexpr range_error(const char* what_arg);

Changes in [overflow.error]

Instructions to the editor:
Please make the following edits to [overflow.error]:
namespace std {
  class overflow_error : public logic_error {
  public:
    constexpr explicit overflow_error(const string& what_arg); // freestanding-consteval
    constexpr explicit overflow_error(const char* what_arg); // freestanding-consteval
  };
}
/*...*/
constexpr overflow_error(const string& what_arg);
/*...*/
constexpr overflow_error(const char* what_arg);

Changes in [underflow.error]

Instructions to the editor:
Please make the following edits to [underflow.error]:
namespace std {
  class underflow_error : public logic_error {
  public:
    constexpr explicit underflow_error(const string& what_arg); // freestanding-consteval
    constexpr explicit underflow_error(const char* what_arg); // freestanding-consteval
  };
}
/*...*/
constexpr underflow_error(const string& what_arg);
/*...*/
constexpr underflow_error(const char* what_arg);

Changes in [memory.syn]

Instructions to the editor:
Please make the following edit to [memory.syn]:
  // [default.allocator], the default allocator
  template<class T> class allocator; // partially freestanding
  template<class T, class U>
    constexpr bool operator==(const allocator<T>&, const allocator<U>&) noexcept; // freestanding

Changes in [default.allocator]

Instructions to the editor:
Please make the following edit to [default.allocator]:
namespace std { template<class T> class allocator { public: using value_type = T; using size_type = size_t; using difference_type = ptrdiff_t; using propagate_on_container_move_assignment = true_type; constexpr allocator() noexcept; constexpr allocator(const allocator&) noexcept; template<class U> constexpr allocator(const allocator<U>&) noexcept; constexpr ~allocator(); constexpr allocator& operator=(const allocator&) = default; constexpr T* allocate(size_t n); // freestanding-consteval constexpr allocation_result<T*> allocate_at_least(size_t n); // freestanding-consteval constexpr void deallocate(T* p, size_t n); // freestanding-consteval }; }

Changes in [allocator.requirements.general]

Instructions to the editor:
Please make the following edit to [allocator.requirements.general]:
a.allocate(n)
Result: XX​::​pointer
Effects: Memory is allocated for an array of n T and such an object is created but array elements are not constructed.
[Example 1: 
When reusing storage denoted by some pointer value p, launder(reinterpret_cast<T*>(new (p) byte[n * sizeof(T)])) can be used to implicitly create a suitable array object and obtain a pointer to it.
— end example]
Throws: allocate may throw an appropriate exception.
[Note 3: 
It is intended that a.allocate be an efficient means of allocating a single object of type T, even when sizeof(T) is small.
That is, there is no need for a container to maintain its own free list.
— end note]
Remarks: If n == 0, the return value is unspecified.
Remarks: Default: On freestanding implementations, XX::pointer().
[Note ??? : This default is never used by allocator_traits, but it ensures that allocator<T> always meets the allocator requirements even though allocate is a freestanding deleted function ([freestanding.item]). — end note]
[...]
a.deallocate(p, n)
Result: (not used)
Preconditions:
  • If p is memory that was obtained by a call to a.allocate_at_least, let ret be the value returned and req be the value passed as the first argument of that call.
    p is equal to ret.ptr and n is a value such that req  ≤ n  ≤ ret.count.
  • Otherwise, p is a pointer value obtained from allocate.
    n equals the value passed as the first argument to the invocation of allocate which returned p.
p has not been invalidated by an intervening call to deallocate.
Throws: Nothing.
Remarks: Default: On freestanding implementations, void().
[Note ??? : This default is never used by allocator_traits, but it ensures that allocator<T> always meets the allocator requirements even though deallocate is a freestanding deleted function ([freestanding.item]). — end note]

Changes in [allocator.traits.members]

Instructions to the editor:
Please make the following edit to [allocator.traits.members]:
static constexpr pointer allocate(Alloc& a, size_type n);
Mandates: a.allocate(n) is well-formed.
Returns: a.allocate(n).
static constexpr pointer allocate(Alloc& a, size_type n, const_void_pointer hint);
Returns: a.allocate(n, hint) if that expression is well-formed; otherwise, a.allocate(n).
static constexpr allocation_result<pointer, size_type> allocate_at_least(Alloc& a, size_type n);
Returns: a.allocate_at_least(n) if that expression is well-formed; otherwise, {a.allocate(n), n}.
static constexpr void deallocate(Alloc& a, pointer p, size_type n);
Mandates: a.deallocate(n) is well-formed.
Effects: Calls a.deallocate(p, n).
Throws: Nothing.

Changes in [string.view.template.general]

Instructions to the editor:
Please make the following edit to [string.view.template.general]:
    // [string.view.access], element access
    constexpr const_reference operator[](size_type pos) const;
    constexpr const_reference at(size_type pos) const;                  // freestanding-deletedconsteval
    constexpr const_reference front() const;
    constexpr const_reference back() const;
    constexpr const_pointer data() const noexcept;

    // [string.view.modifiers], modifiers
    constexpr void remove_prefix(size_type n);
    constexpr void remove_suffix(size_type n);
    constexpr void swap(basic_string_view& s) noexcept;

    // [string.view.ops], string operations
    constexpr size_type copy(charT* s, size_type n,
                             size_type pos = 0) const;                  // freestanding-deletedconsteval

    constexpr basic_string_view substr(size_type pos = 0,
                                       size_type n = npos) const;       // freestanding-deletedconsteval

    constexpr int compare(basic_string_view s) const noexcept;
    constexpr int compare(size_type pos1, size_type n1,
                          basic_string_view s) const;                   // freestanding-deletedconsteval
    constexpr int compare(size_type pos1, size_type n1, basic_string_view s,
                          size_type pos2, size_type n2) const;          // freestanding-deletedconsteval
    constexpr int compare(const charT* s) const;
    constexpr int compare(size_type pos1, size_type n1,
                          const charT* s) const;                        // freestanding-deletedconsteval
    constexpr int compare(size_type pos1, size_type n1, const charT* s,
                          size_type n2) const;                          // freestanding-deletedconsteval

Changes in [string.syn]

Instructions to the editor:
Please make the following edits to [string.syn]:

  // [basic.string], basic_string
  template<class charT, class traits = char_traits<charT>, class Allocator = allocator<charT>>
    class basic_string; // partially freestanding

  template<class charT, class traits, class Allocator>
    constexpr basic_string<charT, traits, Allocator>
      operator+(const basic_string<charT, traits, Allocator>& lhs,
                const basic_string<charT, traits, Allocator>& rhs); // freestanding-consteval
  template<class charT, class traits, class Allocator>
    constexpr basic_string<charT, traits, Allocator>
      operator+(basic_string<charT, traits, Allocator>&& lhs,
                const basic_string<charT, traits, Allocator>& rhs); // freestanding-consteval
  template<class charT, class traits, class Allocator>
    constexpr basic_string<charT, traits, Allocator>
      operator+(const basic_string<charT, traits, Allocator>& lhs,
                basic_string<charT, traits, Allocator>&& rhs); // freestanding-consteval
  template<class charT, class traits, class Allocator>
    constexpr basic_string<charT, traits, Allocator>
      operator+(basic_string<charT, traits, Allocator>&& lhs,
                basic_string<charT, traits, Allocator>&& rhs); // freestanding-consteval
  template<class charT, class traits, class Allocator>
    constexpr basic_string<charT, traits, Allocator>
      operator+(const charT* lhs,
                const basic_string<charT, traits, Allocator>& rhs); // freestanding-consteval
  template<class charT, class traits, class Allocator>
    constexpr basic_string<charT, traits, Allocator>
      operator+(const charT* lhs,
                basic_string<charT, traits, Allocator>&& rhs); // freestanding-consteval
  template<class charT, class traits, class Allocator>
    constexpr basic_string<charT, traits, Allocator>
      operator+(charT lhs,
                const basic_string<charT, traits, Allocator>& rhs); // freestanding-consteval
  template<class charT, class traits, class Allocator>
    constexpr basic_string<charT, traits, Allocator>
      operator+(charT lhs,
                basic_string<charT, traits, Allocator>&& rhs); // freestanding-consteval
  template<class charT, class traits, class Allocator>
    constexpr basic_string<charT, traits, Allocator>
      operator+(const basic_string<charT, traits, Allocator>& lhs,
                const charT* rhs); // freestanding-consteval
  template<class charT, class traits, class Allocator>
    constexpr basic_string<charT, traits, Allocator>
      operator+(basic_string<charT, traits, Allocator>&& lhs,
                const charT* rhs); // freestanding-consteval
  template<class charT, class traits, class Allocator>
    constexpr basic_string<charT, traits, Allocator>
      operator+(const basic_string<charT, traits, Allocator>& lhs,
                charT rhs); // freestanding-consteval
  template<class charT, class traits, class Allocator>
    constexpr basic_string<charT, traits, Allocator>
      operator+(basic_string<charT, traits, Allocator>&& lhs,
                charT rhs); // freestanding-consteval
  template<class charT, class traits, class Allocator>
    constexpr basic_string<charT, traits, Allocator>
      operator+(const basic_string<charT, traits, Allocator>& lhs,
                type_identity_t<basic_string_view<charT, traits>> rhs); // freestanding-consteval
  template<class charT, class traits, class Allocator>
    constexpr basic_string<charT, traits, Allocator>
      operator+(basic_string<charT, traits, Allocator>&& lhs,
                type_identity_t<basic_string_view<charT, traits>> rhs); // freestanding-consteval
  template<class charT, class traits, class Allocator>
    constexpr basic_string<charT, traits, Allocator>
      operator+(type_identity_t<basic_string_view<charT, traits>> lhs,
                const basic_string<charT, traits, Allocator>& rhs); // freestanding-consteval
  template<class charT, class traits, class Allocator>
    constexpr basic_string<charT, traits, Allocator>
      operator+(type_identity_t<basic_string_view<charT, traits>> lhs,
                basic_string<charT, traits, Allocator>&& rhs); // freestanding-consteval

  template<class charT, class traits, class Allocator>
    constexpr bool
      operator==(const basic_string<charT, traits, Allocator>& lhs,
                 const basic_string<charT, traits, Allocator>& rhs) noexcept; // freestanding
  template<class charT, class traits, class Allocator>
    constexpr bool operator==(const basic_string<charT, traits, Allocator>& lhs,
                              const charT* rhs); // freestanding

  template<class charT, class traits, class Allocator>
    constexpr see below operator<=>(const basic_string<charT, traits, Allocator>& lhs,
                                    const basic_string<charT, traits, Allocator>& rhs) noexcept; // freestanding
  template<class charT, class traits, class Allocator>
    constexpr see below operator<=>(const basic_string<charT, traits, Allocator>& lhs,
                                    const charT* rhs); // freestanding

  // [string.special], swap
  template<class charT, class traits, class Allocator>
    constexpr void
      swap(basic_string<charT, traits, Allocator>& lhs,
           basic_string<charT, traits, Allocator>& rhs)
        noexcept(noexcept(lhs.swap(rhs))); // freestanding-consteval

  // [string.io], inserters and extractors
  template<class charT, class traits, class Allocator>
    basic_istream<charT, traits>&
      operator>>(basic_istream<charT, traits>& is,
                 basic_string<charT, traits, Allocator>& str);
  template<class charT, class traits, class Allocator>
    basic_ostream<charT, traits>&
      operator<<(basic_ostream<charT, traits>& os,
                 const basic_string<charT, traits, Allocator>& str);
  template<class charT, class traits, class Allocator>
    basic_istream<charT, traits>&
      getline(basic_istream<charT, traits>& is,
              basic_string<charT, traits, Allocator>& str,
              charT delim);
  template<class charT, class traits, class Allocator>
    basic_istream<charT, traits>&
      getline(basic_istream<charT, traits>&& is,
              basic_string<charT, traits, Allocator>& str,
              charT delim);
  template<class charT, class traits, class Allocator>
    basic_istream<charT, traits>&
      getline(basic_istream<charT, traits>& is,
              basic_string<charT, traits, Allocator>& str);
  template<class charT, class traits, class Allocator>
    basic_istream<charT, traits>&
      getline(basic_istream<charT, traits>&& is,
              basic_string<charT, traits, Allocator>& str);

  // [string.erasure], erasure
  template<class charT, class traits, class Allocator, class U = charT>
    constexpr typename basic_string<charT, traits, Allocator>::size_type
      erase(basic_string<charT, traits, Allocator>& c, const U& value); // freestanding-consteval
  template<class charT, class traits, class Allocator, class Predicate>
    constexpr typename basic_string<charT, traits, Allocator>::size_type
      erase_if(basic_string<charT, traits, Allocator>& c, Predicate pred); // freestanding-consteval

  // basic_string typedef-names
  using string    = basic_string<char>; // freestanding
  using u8string  = basic_string<char8_t>; // freestanding
  using u16string = basic_string<char16_t>; // freestanding
  using u32string = basic_string<char32_t>; // freestanding
  using wstring   = basic_string<wchar_t>; // freestanding
/* ... */
  inline namespace literals {
    inline namespace string_literals {
      // [basic.string.literals], suffix for basic_string literals
      constexpr string    operator""s(const char* str, size_t len); // freestanding-consteval
      constexpr u8string  operator""s(const char8_t* str, size_t len); // freestanding-consteval
      constexpr u16string operator""s(const char16_t* str, size_t len); // freestanding-consteval
      constexpr u32string operator""s(const char32_t* str, size_t len); // freestanding-consteval
      constexpr wstring   operator""s(const wchar_t* str, size_t len); // freestanding-consteval
    }
  }

Changes in [basic.string]

Drafting note:
The following compare overloads are intentionally not marked freestanding-consteval, as they neither mutate nor throw:
Instructions to the editor:
Please make the following edits to [basic.string]:
template<class charT, class traits = char_traits<charT>, class Allocator = allocator<charT>> class basic_string { public: // types using traits_type = traits; using value_type = charT; using allocator_type = Allocator; using size_type = typename allocator_traits<Allocator>::size_type; using difference_type = typename allocator_traits<Allocator>::difference_type; using pointer = typename allocator_traits<Allocator>::pointer; using const_pointer = typename allocator_traits<Allocator>::const_pointer; using reference = value_type&; using const_reference = const value_type&; using iterator = implementation-defined; // see [container.requirements] using const_iterator = implementation-defined; // see [container.requirements] using reverse_iterator = std::reverse_iterator<iterator>; using const_reverse_iterator = std::reverse_iterator<const_iterator>; static constexpr size_type npos = size_type(-1); // [string.cons], construct/copy/destroy constexpr basic_string() noexcept(noexcept(Allocator())) : basic_string(Allocator()) { } // freestanding-consteval constexpr explicit basic_string(const Allocator& a) noexcept; // freestanding-consteval constexpr basic_string(const basic_string& str); // freestanding-consteval constexpr basic_string(basic_string&& str) noexcept; // freestanding-consteval constexpr basic_string(const basic_string& str, size_type pos, const Allocator& a = Allocator()); // freestanding-consteval constexpr basic_string(const basic_string& str, size_type pos, size_type n, const Allocator& a = Allocator()); // freestanding-consteval constexpr basic_string(basic_string&& str, size_type pos, const Allocator& a = Allocator()); // freestanding-consteval constexpr basic_string(basic_string&& str, size_type pos, size_type n, const Allocator& a = Allocator()); // freestanding-consteval template<class T> constexpr basic_string(const T& t, size_type pos, size_type n, const Allocator& a = Allocator()); // freestanding-consteval template<class T> constexpr explicit basic_string(const T& t, const Allocator& a = Allocator()); // freestanding-consteval constexpr basic_string(const charT* s, size_type n, const Allocator& a = Allocator()); // freestanding-consteval constexpr basic_string(const charT* s, const Allocator& a = Allocator()); // freestanding-consteval basic_string(nullptr_t) = delete; constexpr basic_string(size_type n, charT c, const Allocator& a = Allocator()); // freestanding-consteval template<class InputIterator> constexpr basic_string(InputIterator begin, InputIterator end, const Allocator& a = Allocator()); // freestanding-consteval template<container-compatible-range<charT> R> constexpr basic_string(from_range_t, R&& rg, const Allocator& a = Allocator()); // freestanding-consteval constexpr basic_string(initializer_list<charT>, const Allocator& = Allocator()); // freestanding-consteval constexpr basic_string(const basic_string&, const Allocator&); // freestanding-consteval constexpr basic_string(basic_string&&, const Allocator&); // freestanding-consteval constexpr ~basic_string(); constexpr basic_string& operator=(const basic_string& str); // freestanding-consteval constexpr basic_string& operator=(basic_string&& str) noexcept(allocator_traits<Allocator>::propagate_on_container_move_assignment::value || allocator_traits<Allocator>::is_always_equal::value); // freestanding-consteval template<class T> constexpr basic_string& operator=(const T& t); // freestanding-consteval constexpr basic_string& operator=(const charT* s); // freestanding-consteval basic_string& operator=(nullptr_t) = delete; constexpr basic_string& operator=(charT c); // freestanding-consteval constexpr basic_string& operator=(initializer_list<charT>); // freestanding-consteval // [string.iterators], iterators constexpr iterator begin() noexcept; // freestanding-consteval constexpr const_iterator begin() const noexcept; constexpr iterator end() noexcept; // freestanding-consteval constexpr const_iterator end() const noexcept; constexpr reverse_iterator rbegin() noexcept; // freestanding-consteval constexpr const_reverse_iterator rbegin() const noexcept; constexpr reverse_iterator rend() noexcept; // freestanding-consteval constexpr const_reverse_iterator rend() const noexcept; constexpr const_iterator cbegin() const noexcept; constexpr const_iterator cend() const noexcept; constexpr const_reverse_iterator crbegin() const noexcept; constexpr const_reverse_iterator crend() const noexcept; // [string.capacity], capacity constexpr size_type size() const noexcept; constexpr size_type length() const noexcept; constexpr size_type max_size() const noexcept; constexpr void resize(size_type n, charT c); // freestanding-consteval constexpr void resize(size_type n); // freestanding-consteval template<class Operation> constexpr void resize_and_overwrite(size_type n, Operation op); // freestanding-consteval constexpr size_type capacity() const noexcept; constexpr void reserve(size_type res_arg); // freestanding-consteval constexpr void shrink_to_fit(); // freestanding-consteval constexpr void clear() noexcept; // freestanding-consteval constexpr bool empty() const noexcept; // [string.access], element access constexpr const_reference operator[](size_type pos) const; constexpr reference operator[](size_type pos); // freestanding-consteval constexpr const_reference at(size_type n) const; // freestanding-consteval constexpr reference at(size_type n); // freestanding-consteval constexpr const charT& front() const; constexpr charT& front(); // freestanding-consteval constexpr const charT& back() const; constexpr charT& back(); // freestanding-consteval // [string.modifiers], modifiers constexpr basic_string& operator+=(const basic_string& str); // freestanding-consteval template<class T> constexpr basic_string& operator+=(const T& t); // freestanding-consteval constexpr basic_string& operator+=(const charT* s); // freestanding-consteval constexpr basic_string& operator+=(charT c); // freestanding-consteval constexpr basic_string& operator+=(initializer_list<charT>); // freestanding-consteval constexpr basic_string& append(const basic_string& str); // freestanding-consteval constexpr basic_string& append(const basic_string& str, size_type pos, size_type n = npos); // freestanding-consteval template<class T> constexpr basic_string& append(const T& t); // freestanding-consteval template<class T> constexpr basic_string& append(const T& t, size_type pos, size_type n = npos); // freestanding-consteval constexpr basic_string& append(const charT* s, size_type n); // freestanding-consteval constexpr basic_string& append(const charT* s); // freestanding-consteval constexpr basic_string& append(size_type n, charT c); // freestanding-consteval template<class InputIterator> constexpr basic_string& append(InputIterator first, InputIterator last); // freestanding-consteval template<container-compatible-range<charT> R> constexpr basic_string& append_range(R&& rg); // freestanding-consteval constexpr basic_string& append(initializer_list<charT>); // freestanding-consteval constexpr void push_back(charT c); // freestanding-consteval constexpr basic_string& assign(const basic_string& str); // freestanding-consteval constexpr basic_string& assign(basic_string&& str) noexcept(allocator_traits<Allocator>::propagate_on_container_move_assignment::value || allocator_traits<Allocator>::is_always_equal::value); // freestanding-consteval constexpr basic_string& assign(const basic_string& str, size_type pos, size_type n = npos); // freestanding-consteval template<class T> constexpr basic_string& assign(const T& t); // freestanding-consteval template<class T> constexpr basic_string& assign(const T& t, size_type pos, size_type n = npos); // freestanding-consteval constexpr basic_string& assign(const charT* s, size_type n); // freestanding-consteval constexpr basic_string& assign(const charT* s); // freestanding-consteval constexpr basic_string& assign(size_type n, charT c); // freestanding-consteval template<class InputIterator> constexpr basic_string& assign(InputIterator first, InputIterator last); // freestanding-consteval template<container-compatible-range<charT> R> constexpr basic_string& assign_range(R&& rg); // freestanding-consteval constexpr basic_string& assign(initializer_list<charT>); // freestanding-consteval constexpr basic_string& insert(size_type pos, const basic_string& str); // freestanding-consteval constexpr basic_string& insert(size_type pos1, const basic_string& str, size_type pos2, size_type n = npos); // freestanding-consteval template<class T> constexpr basic_string& insert(size_type pos, const T& t); // freestanding-consteval template<class T> constexpr basic_string& insert(size_type pos1, const T& t, size_type pos2, size_type n = npos); // freestanding-consteval constexpr basic_string& insert(size_type pos, const charT* s, size_type n); // freestanding-consteval constexpr basic_string& insert(size_type pos, const charT* s); // freestanding-consteval constexpr basic_string& insert(size_type pos, size_type n, charT c); // freestanding-consteval constexpr iterator insert(const_iterator p, charT c); // freestanding-consteval constexpr iterator insert(const_iterator p, size_type n, charT c); // freestanding-consteval template<class InputIterator> constexpr iterator insert(const_iterator p, InputIterator first, InputIterator last); // freestanding-consteval template<container-compatible-range<charT> R> constexpr iterator insert_range(const_iterator p, R&& rg); // freestanding-consteval constexpr iterator insert(const_iterator p, initializer_list<charT>); // freestanding-consteval constexpr basic_string& erase(size_type pos = 0, size_type n = npos); // freestanding-consteval constexpr iterator erase(const_iterator p); // freestanding-consteval constexpr iterator erase(const_iterator first, const_iterator last); // freestanding-consteval constexpr void pop_back(); // freestanding-consteval constexpr basic_string& replace(size_type pos1, size_type n1, const basic_string& str); // freestanding-consteval constexpr basic_string& replace(size_type pos1, size_type n1, const basic_string& str, size_type pos2, size_type n2 = npos); // freestanding-consteval template<class T> constexpr basic_string& replace(size_type pos1, size_type n1, const T& t); // freestanding-consteval template<class T> constexpr basic_string& replace(size_type pos1, size_type n1, const T& t, size_type pos2, size_type n2 = npos); // freestanding-consteval constexpr basic_string& replace(size_type pos, size_type n1, const charT* s, size_type n2); // freestanding-consteval constexpr basic_string& replace(size_type pos, size_type n1, const charT* s); // freestanding-consteval constexpr basic_string& replace(size_type pos, size_type n1, size_type n2, charT c); // freestanding-consteval constexpr basic_string& replace(const_iterator i1, const_iterator i2, const basic_string& str); // freestanding-consteval template<class T> constexpr basic_string& replace(const_iterator i1, const_iterator i2, const T& t); // freestanding-consteval constexpr basic_string& replace(const_iterator i1, const_iterator i2, const charT* s, size_type n); // freestanding-consteval constexpr basic_string& replace(const_iterator i1, const_iterator i2, const charT* s); // freestanding-consteval constexpr basic_string& replace(const_iterator i1, const_iterator i2, size_type n, charT c); // freestanding-consteval template<class InputIterator> constexpr basic_string& replace(const_iterator i1, const_iterator i2, InputIterator j1, InputIterator j2); // freestanding-consteval template<container-compatible-range<charT> R> constexpr basic_string& replace_with_range(const_iterator i1, const_iterator i2, R&& rg); // freestanding-consteval constexpr basic_string& replace(const_iterator, const_iterator, initializer_list<charT>); // freestanding-consteval constexpr size_type copy(charT* s, size_type n, size_type pos = 0) const; // freestanding-consteval constexpr void swap(basic_string& str) noexcept(allocator_traits<Allocator>::propagate_on_container_swap::value || allocator_traits<Allocator>::is_always_equal::value); // freestanding-consteval // [string.ops], string operations constexpr const charT* c_str() const noexcept; constexpr const charT* data() const noexcept; constexpr charT* data() noexcept; // freestanding-consteval constexpr operator basic_string_view<charT, traits>() const noexcept; constexpr allocator_type get_allocator() const noexcept; template<class T> constexpr size_type find(const T& t, size_type pos = 0) const noexcept(see below); constexpr size_type find(const basic_string& str, size_type pos = 0) const noexcept; constexpr size_type find(const charT* s, size_type pos, size_type n) const; constexpr size_type find(const charT* s, size_type pos = 0) const; constexpr size_type find(charT c, size_type pos = 0) const noexcept; template<class T> constexpr size_type rfind(const T& t, size_type pos = npos) const noexcept(see below); constexpr size_type rfind(const basic_string& str, size_type pos = npos) const noexcept; constexpr size_type rfind(const charT* s, size_type pos, size_type n) const; constexpr size_type rfind(const charT* s, size_type pos = npos) const; constexpr size_type rfind(charT c, size_type pos = npos) const noexcept; template<class T> constexpr size_type find_first_of(const T& t, size_type pos = 0) const noexcept(see below); constexpr size_type find_first_of(const basic_string& str, size_type pos = 0) const noexcept; constexpr size_type find_first_of(const charT* s, size_type pos, size_type n) const; constexpr size_type find_first_of(const charT* s, size_type pos = 0) const; constexpr size_type find_first_of(charT c, size_type pos = 0) const noexcept; template<class T> constexpr size_type find_last_of(const T& t, size_type pos = npos) const noexcept(see below); constexpr size_type find_last_of(const basic_string& str, size_type pos = npos) const noexcept; constexpr size_type find_last_of(const charT* s, size_type pos, size_type n) const; constexpr size_type find_last_of(const charT* s, size_type pos = npos) const; constexpr size_type find_last_of(charT c, size_type pos = npos) const noexcept; template<class T> constexpr size_type find_first_not_of(const T& t, size_type pos = 0) const noexcept(see below); constexpr size_type find_first_not_of(const basic_string& str, size_type pos = 0) const noexcept; constexpr size_type find_first_not_of(const charT* s, size_type pos, size_type n) const; constexpr size_type find_first_not_of(const charT* s, size_type pos = 0) const; constexpr size_type find_first_not_of(charT c, size_type pos = 0) const noexcept; template<class T> constexpr size_type find_last_not_of(const T& t, size_type pos = npos) const noexcept(see below); constexpr size_type find_last_not_of(const basic_string& str, size_type pos = npos) const noexcept; constexpr size_type find_last_not_of(const charT* s, size_type pos, size_type n) const; constexpr size_type find_last_not_of(const charT* s, size_type pos = npos) const; constexpr size_type find_last_not_of(charT c, size_type pos = npos) const noexcept; constexpr basic_string substr(size_type pos = 0, size_type n = npos) const &; // freestanding-consteval constexpr basic_string substr(size_type pos = 0, size_type n = npos) &&; // freestanding-consteval template<class T> constexpr int compare(const T& t) const noexcept(see below); template<class T> constexpr int compare(size_type pos1, size_type n1, const T& t) const; // freestanding-consteval template<class T> constexpr int compare(size_type pos1, size_type n1, const T& t, size_type pos2, size_type n2 = npos) const; // freestanding-consteval constexpr int compare(const basic_string& str) const noexcept; constexpr int compare(size_type pos1, size_type n1, const basic_string& str) const; // freestanding-consteval constexpr int compare(size_type pos1, size_type n1, const basic_string& str, size_type pos2, size_type n2 = npos) const; // freestanding-consteval constexpr int compare(const charT* s) const; constexpr int compare(size_type pos1, size_type n1, const charT* s) const; // freestanding-consteval constexpr int compare(size_type pos1, size_type n1, const charT* s, size_type n2) const; // freestanding-consteval constexpr bool starts_with(basic_string_view<charT, traits> x) const noexcept; constexpr bool starts_with(charT x) const noexcept; constexpr bool starts_with(const charT* x) const; constexpr bool ends_with(basic_string_view<charT, traits> x) const noexcept; constexpr bool ends_with(charT x) const noexcept; constexpr bool ends_with(const charT* x) const; constexpr bool contains(basic_string_view<charT, traits> x) const noexcept; constexpr bool contains(charT x) const noexcept; constexpr bool contains(const charT* x) const; };

Changes in [vector.syn]

Instructions to the editor:
Please make the following edits to [vector.syn]:

  // [vector], class template vector
  template<class T, class Allocator = allocator<T>> class vector; // partially freestanding

  template<class T, class Allocator>
    constexpr bool operator==(const vector<T, Allocator>& x, const vector<T, Allocator>& y); // freestanding
  template<class T, class Allocator>
    constexpr synth-three-way-result<T> operator<=>(const vector<T, Allocator>& x,
                                                    const vector<T, Allocator>& y); // freestanding

  template<class T, class Allocator>
    constexpr void swap(vector<T, Allocator>& x, vector<T, Allocator>& y)
      noexcept(noexcept(x.swap(y))); // freestanding-consteval

  // [vector.erasure], erasure
  template<class T, class Allocator, class U = T>
    constexpr typename vector<T, Allocator>::size_type
      erase(vector<T, Allocator>& c, const U& value); // freestanding-consteval
  template<class T, class Allocator, class Predicate>
    constexpr typename vector<T, Allocator>::size_type
      erase_if(vector<T, Allocator>& c, Predicate pred); // freestanding-consteval

  namespace pmr {
    template<class T>
      using vector = std::vector<T, polymorphic_allocator<T>>;
  }

  // [vector.bool], specialization of vector for bool
  // [vector.bool.pspc], partial class template specialization vector<bool, Allocator>
  template<class Allocator>
    class vector<bool, Allocator>; // partially freestanding

Changes in [vector]

Instructions to the editor:
Please make the following edits to [vector]:
template<class T, class Allocator = allocator<T>> class vector { public: // types using value_type = T; using allocator_type = Allocator; using pointer = typename allocator_traits<Allocator>::pointer; using const_pointer = typename allocator_traits<Allocator>::const_pointer; using reference = value_type&; using const_reference = const value_type&; using size_type = implementation-defined; // see [container.requirements] using difference_type = implementation-defined; // see [container.requirements] using iterator = implementation-defined; // see [container.requirements] using const_iterator = implementation-defined; // see [container.requirements] using reverse_iterator = std::reverse_iterator<iterator>; using const_reverse_iterator = std::reverse_iterator<const_iterator>; // [vector.cons], construct/copy/destroy constexpr vector() noexcept(noexcept(Allocator())) : vector(Allocator()) { } // freestanding-consteval constexpr explicit vector(const Allocator&) noexcept; // freestanding-consteval constexpr explicit vector(size_type n, const Allocator& = Allocator()); // freestanding-consteval constexpr vector(size_type n, const T& value, const Allocator& = Allocator()); // freestanding-consteval template<class InputIterator> constexpr vector(InputIterator first, InputIterator last, const Allocator& = Allocator()); // freestanding-consteval template<container-compatible-range<T> R> constexpr vector(from_range_t, R&& rg, const Allocator& = Allocator()); // freestanding-consteval constexpr vector(const vector& x); // freestanding-consteval constexpr vector(vector&&) noexcept; // freestanding-consteval constexpr vector(const vector&, const type_identity_t<Allocator>&); // freestanding-consteval constexpr vector(vector&&, const type_identity_t<Allocator>&); // freestanding-consteval constexpr vector(initializer_list<T>, const Allocator& = Allocator()); // freestanding-consteval constexpr ~vector(); constexpr vector& operator=(const vector& x); // freestanding-consteval constexpr vector& operator=(vector&& x) noexcept(allocator_traits<Allocator>::propagate_on_container_move_assignment::value || allocator_traits<Allocator>::is_always_equal::value); // freestanding-consteval constexpr vector& operator=(initializer_list<T>); // freestanding-consteval template<class InputIterator> constexpr void assign(InputIterator first, InputIterator last); // freestanding-consteval template<container-compatible-range<T> R> constexpr void assign_range(R&& rg); // freestanding-consteval constexpr void assign(size_type n, const T& u); // freestanding-consteval constexpr void assign(initializer_list<T>); // freestanding-consteval constexpr allocator_type get_allocator() const noexcept; // iterators constexpr iterator begin() noexcept; // freestanding-consteval constexpr const_iterator begin() const noexcept; constexpr iterator end() noexcept; // freestanding-consteval constexpr const_iterator end() const noexcept; constexpr reverse_iterator rbegin() noexcept; // freestanding-consteval constexpr const_reverse_iterator rbegin() const noexcept; constexpr reverse_iterator rend() noexcept; // freestanding-consteval constexpr const_reverse_iterator rend() const noexcept; constexpr const_iterator cbegin() const noexcept; constexpr const_iterator cend() const noexcept; constexpr const_reverse_iterator crbegin() const noexcept; constexpr const_reverse_iterator crend() const noexcept; // [vector.capacity], capacity constexpr bool empty() const noexcept; constexpr size_type size() const noexcept; constexpr size_type max_size() const noexcept; constexpr size_type capacity() const noexcept; constexpr void resize(size_type sz); // freestanding-consteval constexpr void resize(size_type sz, const T& c); // freestanding-consteval constexpr void reserve(size_type n); // freestanding-consteval constexpr void shrink_to_fit(); // freestanding-consteval // element access constexpr reference operator[](size_type n); // freestanding-consteval constexpr const_reference operator[](size_type n) const; constexpr const_reference at(size_type n) const; // freestanding-consteval constexpr reference at(size_type n); // freestanding-consteval constexpr reference front(); // freestanding-consteval constexpr const_reference front() const; constexpr reference back(); // freestanding-consteval constexpr const_reference back() const; // [vector.data], data access constexpr T* data() noexcept; // freestanding-consteval constexpr const T* data() const noexcept; // [vector.modifiers], modifiers template<class... Args> constexpr reference emplace_back(Args&&... args); constexpr void push_back(const T& x); // freestanding-consteval constexpr void push_back(T&& x); // freestanding-consteval template<container-compatible-range<T> R> constexpr void append_range(R&& rg); // freestanding-consteval constexpr void pop_back(); // freestanding-consteval template<class... Args> constexpr iterator emplace(const_iterator position, Args&&... args); // freestanding-consteval constexpr iterator insert(const_iterator position, const T& x); // freestanding-consteval constexpr iterator insert(const_iterator position, T&& x); // freestanding-consteval constexpr iterator insert(const_iterator position, size_type n, const T& x); // freestanding-consteval template<class InputIterator> constexpr iterator insert(const_iterator position, InputIterator first, InputIterator last); // freestanding-consteval template<container-compatible-range<T> R> constexpr iterator insert_range(const_iterator position, R&& rg); // freestanding-consteval constexpr iterator insert(const_iterator position, initializer_list<T> il); // freestanding-consteval constexpr iterator erase(const_iterator position); // freestanding-consteval constexpr iterator erase(const_iterator first, const_iterator last); // freestanding-consteval constexpr void swap(vector&) noexcept(allocator_traits<Allocator>::propagate_on_container_swap::value || allocator_traits<Allocator>::is_always_equal::value); // freestanding-consteval constexpr void clear() noexcept; // freestanding-consteval };

Changes in [vector.bool]

Instructions to the editor:
Please make the following edits to [vector.bool]:
namespace std { template<class Allocator> class vector<bool, Allocator> { public: // types using value_type = bool; using allocator_type = Allocator; using pointer = implementation-defined; using const_pointer = implementation-defined; using const_reference = bool; using size_type = implementation-defined; // see [container.requirements] using difference_type = implementation-defined; // see [container.requirements] using iterator = implementation-defined; // see [container.requirements] using const_iterator = implementation-defined; // see [container.requirements] using reverse_iterator = std::reverse_iterator<iterator>; using const_reverse_iterator = std::reverse_iterator<const_iterator>; // bit reference class reference { friend class vector; constexpr reference() noexcept; public: constexpr reference(const reference&) = default; constexpr ~reference(); constexpr operator bool() const noexcept; constexpr reference& operator=(bool x) noexcept; constexpr reference& operator=(const reference& x) noexcept; constexpr const reference& operator=(bool x) const noexcept; constexpr void flip() noexcept; // flips the bit }; // construct/copy/destroy constexpr vector() noexcept(noexcept(Allocator())) : vector(Allocator()) { } // freestanding-consteval constexpr explicit vector(const Allocator&) noexcept; // freestanding-consteval constexpr explicit vector(size_type n, const Allocator& = Allocator()); // freestanding-consteval constexpr vector(size_type n, const bool& value, const Allocator& = Allocator()); // freestanding-consteval template<class InputIterator> constexpr vector(InputIterator first, InputIterator last, const Allocator& = Allocator()); // freestanding-consteval template<container-compatible-range<bool> R> constexpr vector(from_range_t, R&& rg, const Allocator& = Allocator()); // freestanding-consteval constexpr vector(const vector& x); // freestanding-consteval constexpr vector(vector&& x) noexcept; // freestanding-consteval constexpr vector(const vector&, const type_identity_t<Allocator>&); // freestanding-consteval constexpr vector(vector&&, const type_identity_t<Allocator>&); // freestanding-consteval constexpr vector(initializer_list<bool>, const Allocator& = Allocator()); // freestanding-consteval constexpr ~vector(); constexpr vector& operator=(const vector& x); // freestanding-consteval constexpr vector& operator=(vector&& x) noexcept(allocator_traits<Allocator>::propagate_on_container_move_assignment::value || allocator_traits<Allocator>::is_always_equal::value); // freestanding-consteval constexpr vector& operator=(initializer_list<bool>); // freestanding-consteval template<class InputIterator> constexpr void assign(InputIterator first, InputIterator last); // freestanding-consteval template<container-compatible-range<bool> R> constexpr void assign_range(R&& rg); // freestanding-consteval constexpr void assign(size_type n, const bool& t); // freestanding-consteval constexpr void assign(initializer_list<bool>); // freestanding-consteval constexpr allocator_type get_allocator() const noexcept; // iterators constexpr iterator begin() noexcept; // freestanding-consteval constexpr const_iterator begin() const noexcept; constexpr iterator end() noexcept; // freestanding-consteval constexpr const_iterator end() const noexcept; constexpr reverse_iterator rbegin() noexcept; // freestanding-consteval constexpr const_reverse_iterator rbegin() const noexcept; constexpr reverse_iterator rend() noexcept; // freestanding-consteval constexpr const_reverse_iterator rend() const noexcept; constexpr const_iterator cbegin() const noexcept; constexpr const_iterator cend() const noexcept; constexpr const_reverse_iterator crbegin() const noexcept; constexpr const_reverse_iterator crend() const noexcept; // capacity constexpr bool empty() const noexcept; constexpr size_type size() const noexcept; constexpr size_type max_size() const noexcept; constexpr size_type capacity() const noexcept; constexpr void resize(size_type sz, bool c = false); // freestanding-consteval constexpr void reserve(size_type n); // freestanding-consteval constexpr void shrink_to_fit(); // freestanding-consteval // element access constexpr reference operator[](size_type n); // freestanding-consteval constexpr const_reference operator[](size_type n) const; constexpr const_reference at(size_type n) const; // freestanding-consteval constexpr reference at(size_type n); // freestanding-consteval constexpr reference front(); // freestanding-consteval constexpr const_reference front() const; constexpr reference back(); // freestanding-consteval constexpr const_reference back() const; // modifiers template<class... Args> constexpr reference emplace_back(Args&&... args); // freestanding-consteval constexpr void push_back(const bool& x); // freestanding-consteval template<container-compatible-range<bool> R> constexpr void append_range(R&& rg); // freestanding-consteval constexpr void pop_back(); // freestanding-consteval template<class... Args> constexpr iterator emplace(const_iterator position, Args&&... args); // freestanding-consteval constexpr iterator insert(const_iterator position, const bool& x); // freestanding-consteval constexpr iterator insert(const_iterator position, size_type n, const bool& x); // freestanding-consteval template<class InputIterator> constexpr iterator insert(const_iterator position, InputIterator first, InputIterator last); // freestanding-consteval template<container-compatible-range<bool> R> constexpr iterator insert_range(const_iterator position, R&& rg); // freestanding-consteval constexpr iterator insert(const_iterator position, initializer_list<bool> il); // freestanding-consteval constexpr iterator erase(const_iterator position); // freestanding-consteval constexpr iterator erase(const_iterator first, const_iterator last); // freestanding-consteval constexpr void swap(vector&) noexcept(allocator_traits<Allocator>::propagate_on_container_swap::value || allocator_traits<Allocator>::is_always_equal::value); // freestanding-consteval static constexpr void swap(reference x, reference y) noexcept; // freestanding-consteval constexpr void flip() noexcept; // flips all bits, freestanding-consteval constexpr void clear() noexcept; // freestanding-consteval }; }