[format.formatter]

# 28 Text processing library [[text]](./#text)

## 28.5 Formatting [[format]](format#formatter)

### 28.5.6 Formatter [format.formatter]

#### [28.5.6.1](#formatter.requirements) Formatter requirements [[formatter.requirements]](formatter.requirements)

[1](#formatter.requirements-1)

[#](http://github.com/Eelis/draft/tree/9adde4bc1c62ec234483e63ea3b70a59724c745a/source/text.tex#L6981)

A type F meets the *BasicFormatter* requirements if
it meets the

- [(1.1)](#formatter.requirements-1.1)

*Cpp17DefaultConstructible* (Table [30](utility.arg.requirements#tab:cpp17.defaultconstructible "Table 30: Cpp17DefaultConstructible requirements")),

- [(1.2)](#formatter.requirements-1.2)

*Cpp17CopyConstructible* (Table [32](utility.arg.requirements#tab:cpp17.copyconstructible "Table 32: Cpp17CopyConstructible requirements (in addition to Cpp17MoveConstructible)")),

- [(1.3)](#formatter.requirements-1.3)

*Cpp17CopyAssignable* (Table [34](utility.arg.requirements#tab:cpp17.copyassignable "Table 34: Cpp17CopyAssignable requirements (in addition to Cpp17MoveAssignable)")),

- [(1.4)](#formatter.requirements-1.4)

*Cpp17Swappable* ([[swappable.requirements]](swappable.requirements "16.4.4.3 Swappable requirements")), and

- [(1.5)](#formatter.requirements-1.5)

*Cpp17Destructible* (Table [35](utility.arg.requirements#tab:cpp17.destructible "Table 35: Cpp17Destructible requirements"))

requirements, and
the expressions shown in Table [112](#tab:formatter.basic "Table 112: BasicFormatter requirements") are valid and
have the indicated semantics[.](#formatter.requirements-1.sentence-1)

[2](#formatter.requirements-2)

[#](http://github.com/Eelis/draft/tree/9adde4bc1c62ec234483e63ea3b70a59724c745a/source/text.tex#L6995)

A type F meets the *Formatter* requirements
if it meets the *BasicFormatter* requirements and
the expressions shown in Table [113](#tab:formatter "Table 113: Formatter requirements") are valid and
have the indicated semantics[.](#formatter.requirements-2.sentence-1)

[3](#formatter.requirements-3)

[#](http://github.com/Eelis/draft/tree/9adde4bc1c62ec234483e63ea3b70a59724c745a/source/text.tex#L7001)

Given character type charT, output iterator typeOut, and formatting argument type T,
in Table [112](#tab:formatter.basic "Table 112: BasicFormatter requirements") and Table [113](#tab:formatter "Table 113: Formatter requirements"):

- [(3.1)](#formatter.requirements-3.1)

f is a value of type (possibly const) F,

- [(3.2)](#formatter.requirements-3.2)

g is an lvalue of type F,

- [(3.3)](#formatter.requirements-3.3)

u is an lvalue of type T,

- [(3.4)](#formatter.requirements-3.4)

t is a value of a type convertible to (possibly const) T,

- [(3.5)](#formatter.requirements-3.5)

PC is basic_format_parse_context<charT>,

- [(3.6)](#formatter.requirements-3.6)

FC is basic_format_context<Out, charT>,

- [(3.7)](#formatter.requirements-3.7)

pc is an lvalue of type PC, and

- [(3.8)](#formatter.requirements-3.8)

fc is an lvalue of type FC[.](#formatter.requirements-3.sentence-1)

pc.begin() points to the beginning of the*format-spec* ([[format.string]](format.string "28.5.2 Format string"))
of the replacement field being formatted
in the format string[.](#formatter.requirements-3.sentence-2)

If *format-spec* is not present or empty then eitherpc.begin() == pc.end() or*pc.begin() == '}'[.](#formatter.requirements-3.sentence-3)

Table [112](#tab:formatter.basic) — *BasicFormatter* requirements [[tab:formatter.basic]](./tab:formatter.basic)  

| [ð](#tab:formatter.basic-row-1)<br>**Expression** | **Return type** | **Requirement** |
| --- | --- | --- |
| [ð](#tab:formatter.basic-row-2)<br>g.parse(pc) | PC​::​iterator | Parses *format-spec* ([[format.string]](format.string "28.5.2 Format string")) for type T in the range [pc.begin(), pc.end()) until the first unmatched character[.](#tab:formatter.basic-row-2-column-3-sentence-1)<br>Throws format_error unless the whole range is parsed or the unmatched character is }[.](#tab:formatter.basic-row-2-column-3-sentence-2)<br>[*Note [1](#tab:formatter.basic-row-2-column-3-note-1)*:<br>This allows formatters to emit meaningful error messages[.](#tab:formatter.basic-row-2-column-3-sentence-3) â *end note*]<br> Stores the parsed format specifiers in *this and returns an iterator past the end of the parsed range[.](#tab:formatter.basic-row-2-column-3-sentence-4) |
| [ð](#tab:formatter.basic-row-3)<br>f.format(u, fc) | FC​::​iterator | Formats u according to the specifiers stored in *this, writes the output to fc.out(), and returns an iterator past the end of the output range[.](#tab:formatter.basic-row-3-column-3-sentence-1)<br>The output shall only depend on u, fc.locale(), fc.arg(n) for any value n of type size_t, and the range [pc.begin(), pc.end()) from the last call to f.parse(pc)[.](#tab:formatter.basic-row-3-column-3-sentence-2) |

Table [113](#tab:formatter) — *Formatter* requirements [[tab:formatter]](./tab:formatter)  

| [ð](#tab:formatter-row-1)<br>**Expression** | **Return type** | **Requirement** |
| --- | --- | --- |
| [ð](#tab:formatter-row-2)<br>f.format(t, fc) | FC​::​iterator | Formats t according to the specifiers stored in *this, writes the output to fc.out(), and returns an iterator past the end of the output range[.](#tab:formatter-row-2-column-3-sentence-1)<br>The output shall only depend on t, fc.locale(), fc.arg(n) for any value n of type size_t, and the range [pc.begin(), pc.end()) from the last call to f.parse(pc)[.](#tab:formatter-row-2-column-3-sentence-2) |
| [ð](#tab:formatter-row-3)<br>f.format(u, fc) | FC​::​iterator | As above, but does not modify u[.](#tab:formatter-row-3-column-3-sentence-1) |

#### [28.5.6.2](#locking) Formatter locking [[format.formatter.locking]](format.formatter.locking)

[ð](#lib:enable_nonlocking_formatter_optimization)

`template<class T>
  constexpr bool enable_nonlocking_formatter_optimization = false;
`

[1](#locking-1)

[#](http://github.com/Eelis/draft/tree/9adde4bc1c62ec234483e63ea3b70a59724c745a/source/text.tex#L7086)

*Remarks*: Pursuant to [[namespace.std]](namespace.std "16.4.5.2.1 Namespace std"),
users may specialize enable_nonlocking_formatter_optimization for
cv-unqualified program-defined types[.](#locking-1.sentence-1)

Such specializations shall be usable in constant expressions ([[expr.const]](expr.const "7.7 Constant expressions"))
and have type const bool[.](#locking-1.sentence-2)

#### [28.5.6.3](#format.formattable) Concept formattable [[format.formattable]](format.formattable)

[1](#format.formattable-1)

[#](http://github.com/Eelis/draft/tree/9adde4bc1c62ec234483e63ea3b70a59724c745a/source/text.tex#L7097)

Let *fmt-iter-for*<charT> be an unspecified type
that models[output_iterator](iterator.concept.output#concept:output_iterator "24.3.4.10 Concept output_­iterator [iterator.concept.output]")<const charT&> ([[iterator.concept.output]](iterator.concept.output "24.3.4.10 Concept output_­iterator"))[.](#format.formattable-1.sentence-1)

template<class T, class Context, class Formatter = typename Context::template formatter_type<remove_const_t<T>>>concept [*formattable-with*](#concept:formattable-with "28.5.6.3 Concept formattable [format.formattable]") = // *exposition only*[semiregular](concepts.object#concept:semiregular "18.6 Object concepts [concepts.object]")<Formatter> &&requires(Formatter& f, const Formatter& cf, T&& t, Context fc,
 basic_format_parse_context<typename Context::char_type> pc){{ f.parse(pc) } -> [same_as](concept.same#concept:same_as "18.4.2 Concept same_­as [concept.same]")<typename decltype(pc)::iterator>; { cf.format(t, fc) } -> [same_as](concept.same#concept:same_as "18.4.2 Concept same_­as [concept.same]")<typename Context::iterator>; };

template<class T, class charT>concept [formattable](#concept:formattable "28.5.6.3 Concept formattable [format.formattable]") =[*formattable-with*](#concept:formattable-with "28.5.6.3 Concept formattable [format.formattable]")<remove_reference_t<T>, basic_format_context<*fmt-iter-for*<charT>, charT>>;

[2](#format.formattable-2)

[#](http://github.com/Eelis/draft/tree/9adde4bc1c62ec234483e63ea3b70a59724c745a/source/text.tex#L7119)

A type T and a character type charT model [formattable](#concept:formattable "28.5.6.3 Concept formattable [format.formattable]") if formatter<remove_cvref_t<T>, charT> meets
the *BasicFormatter* requirements ([[formatter.requirements]](#formatter.requirements "28.5.6.1 Formatter requirements"))
and, if remove_reference_t<T> is const-qualified,
the *Formatter* requirements[.](#format.formattable-2.sentence-1)

#### [28.5.6.4](#spec) Formatter specializations [[format.formatter.spec]](format.formatter.spec)

[1](#spec-1)

[#](http://github.com/Eelis/draft/tree/9adde4bc1c62ec234483e63ea3b70a59724c745a/source/text.tex#L7130)

The functions defined in [[format.functions]](format.functions "28.5.5 Formatting functions") use
specializations of the class template formatter to format
individual arguments[.](#spec-1.sentence-1)

[2](#spec-2)

[#](http://github.com/Eelis/draft/tree/9adde4bc1c62ec234483e63ea3b70a59724c745a/source/text.tex#L7136)

Let charT be either char or wchar_t[.](#spec-2.sentence-1)

Each specialization of formatter is either enabled or disabled,
as described below[.](#spec-2.sentence-2)

A [*debug-enabled*](#def:debug-enabled "28.5.6.4 Formatter specializations [format.formatter.spec]") specialization of formatter additionally provides
a public, constexpr, non-static member function set_debug_format() which modifies the state of the formatter to be as if
the type of the *std-format-spec* parsed by the last call to parse were ?[.](#spec-2.sentence-3)

Each header that declares the template formatter provides the following enabled specializations:

- [(2.1)](#spec-2.1)

The debug-enabled specializationstemplate<> struct formatter<char, char>;template<> struct formatter<char, wchar_t>;template<> struct formatter<wchar_t, wchar_t>;

- [(2.2)](#spec-2.2)

For each charT,
the debug-enabled string type specializationstemplate<> struct formatter<charT*, charT>;template<> struct formatter<const charT*, charT>;template<size_t N> struct formatter<charT[N], charT>;template<class traits, class Allocator>struct formatter<basic_string<charT, traits, Allocator>, charT>;template<class traits>struct formatter<basic_string_view<charT, traits>, charT>;

- [(2.3)](#spec-2.3)

For each charT,
for each cv-unqualified arithmetic type ArithmeticT other thanchar,wchar_t,char8_t,char16_t, orchar32_t,
a specializationtemplate<> struct formatter<ArithmeticT, charT>;

- [(2.4)](#spec-2.4)

For each charT,
the pointer type specializationstemplate<> struct formatter<nullptr_t, charT>;template<> struct formatter<void*, charT>;template<> struct formatter<const void*, charT>;

The parse member functions of these formatters
interpret the format specification
as a *std-format-spec* as described in [[format.string.std]](format.string.std "28.5.2.2 Standard format specifiers")[.](#spec-2.sentence-4)

[3](#spec-3)

[#](http://github.com/Eelis/draft/tree/9adde4bc1c62ec234483e63ea3b70a59724c745a/source/text.tex#L7204)

Unless specified otherwise, for each type T for which
a formatter specialization is provided by the library,
each of the headers provides the following specialization:template<> inline constexpr bool enable_nonlocking_formatter_optimization<T> = true;

[*Note [1](#spec-note-1)*:

Specializations such as formatter<wchar_t, char> that would require implicit
multibyte / wide string or character conversion are disabled[.](#spec-3.sentence-1)

â *end note*]

[4](#spec-4)

[#](http://github.com/Eelis/draft/tree/9adde4bc1c62ec234483e63ea3b70a59724c745a/source/text.tex#L7217)

The header <format> provides
the following disabled specializations:

- [(4.1)](#spec-4.1)

The string type specializationstemplate<> struct formatter<char*, wchar_t>;template<> struct formatter<const char*, wchar_t>;template<size_t N> struct formatter<char[N], wchar_t>;template<class traits, class Allocator>struct formatter<basic_string<char, traits, Allocator>, wchar_t>;template<class traits>struct formatter<basic_string_view<char, traits>, wchar_t>;

[5](#spec-5)

[#](http://github.com/Eelis/draft/tree/9adde4bc1c62ec234483e63ea3b70a59724c745a/source/text.tex#L7234)

For any types T and charT for which
neither the library nor the user provides
an explicit or partial specialization of
the class template formatter,formatter<T, charT> is disabled[.](#spec-5.sentence-1)

[6](#spec-6)

[#](http://github.com/Eelis/draft/tree/9adde4bc1c62ec234483e63ea3b70a59724c745a/source/text.tex#L7241)

If the library provides an explicit or partial specialization offormatter<T, charT>, that specialization is enabled
and meets the *Formatter* requirements
except as noted otherwise[.](#spec-6.sentence-1)

[7](#spec-7)

[#](http://github.com/Eelis/draft/tree/9adde4bc1c62ec234483e63ea3b70a59724c745a/source/text.tex#L7247)

If F is a disabled specialization of formatter, these
values are false:

- [(7.1)](#spec-7.1)

is_default_constructible_v<F>,

- [(7.2)](#spec-7.2)

is_copy_constructible_v<F>,

- [(7.3)](#spec-7.3)

is_move_constructible_v<F>,

- [(7.4)](#spec-7.4)

is_copy_assignable_v<F>, and

- [(7.5)](#spec-7.5)

is_move_assignable_v<F>[.](#spec-7.sentence-1)

[8](#spec-8)

[#](http://github.com/Eelis/draft/tree/9adde4bc1c62ec234483e63ea3b70a59724c745a/source/text.tex#L7258)

An enabled specialization formatter<T, charT> meets the*BasicFormatter* requirements ([[formatter.requirements]](#formatter.requirements "28.5.6.1 Formatter requirements"))[.](#spec-8.sentence-1)

[*Example [1](#spec-example-1)*: #include <format>#include <string>enum color { red, green, blue };const char* color_names[] = { "red", "green", "blue" };

template<> struct std::formatter<color> : std::formatter<const char*> {auto format(color c, format_context& ctx) const {return formatter<const char*>::format(color_names[c], ctx); }};

struct err {};

std::string s0 = std::format("{}", 42); // OK, library-provided formatter std::string s1 = std::format("{}", L"foo"); // error: disabled formatter std::string s2 = std::format("{}", red); // OK, user-provided formatter std::string s3 = std::format("{}", err{}); // error: disabled formatter â *end example*]

#### [28.5.6.5](#format.string.escaped) Formatting escaped characters and strings [[format.string.escaped]](format.string.escaped)

[1](#format.string.escaped-1)

[#](http://github.com/Eelis/draft/tree/9adde4bc1c62ec234483e63ea3b70a59724c745a/source/text.tex#L7286)

A character or string can be formatted as [*escaped*](#def:escaped "28.5.6.5 Formatting escaped characters and strings [format.string.escaped]") to make it more suitable for debugging or for logging[.](#format.string.escaped-1.sentence-1)

[2](#format.string.escaped-2)

[#](http://github.com/Eelis/draft/tree/9adde4bc1c62ec234483e63ea3b70a59724c745a/source/text.tex#L7292)

The escaped string *E* representation of a string *S* is constructed by encoding a sequence of characters as follows[.](#format.string.escaped-2.sentence-1)

The associated character encoding *CE* for charT (Table [12](lex.string#tab:lex.string.literal "Table 12: String literals"))
is used to both interpret *S* and construct *E*[.](#format.string.escaped-2.sentence-2)

- [(2.1)](#format.string.escaped-2.1)

  U+0022 quotation mark (") is appended to *E*[.](#format.string.escaped-2.1.sentence-1)

- [(2.2)](#format.string.escaped-2.2)

  For each code unit sequence *X* in *S* that either
encodes a single character,
is a shift sequence, or
is a sequence of ill-formed code units,
processing is in order as follows:

  * [(2.2.1)](#format.string.escaped-2.2.1)

      If *X* encodes a single character *C*, then:

    +
          [(2.2.1.1)](#format.string.escaped-2.2.1.1)
          If *C* is one of the characters in Table [114](#tab:format.escape.sequences "Table 114: Mapping of characters to escape sequences"),
then the two characters shown as the corresponding escape sequence
are appended to *E*[.](#format.string.escaped-2.2.1.1.sentence-1)

    +
          [(2.2.1.2)](#format.string.escaped-2.2.1.2)
          Otherwise, if *C* is not U+0020 space and

      - [(2.2.1.2.1)](#format.string.escaped-2.2.1.2.1)

*CE* is UTF-8, UTF-16, or UTF-32 and*C* corresponds to a Unicode scalar value
whose Unicode property General_Category has a value in the groupsSeparator (Z) or Other (C),
as described by UAX #44 of the Unicode Standard, or

      - [(2.2.1.2.2)](#format.string.escaped-2.2.1.2.2)

*CE* is UTF-8, UTF-16, or UTF-32 and*C* corresponds to a Unicode scalar value
with the Unicode property Grapheme_Extend=Yes as described by UAX #44 of the Unicode Standard and*C* is not immediately preceded in *S* by
a character *P* appended to *E* without translation to an escape sequence, or

      - [(2.2.1.2.3)](#format.string.escaped-2.2.1.2.3)

*CE* is neither UTF-8, UTF-16, nor UTF-32 and*C* is one of an implementation-defined set
of separator or non-printable characters

          then the sequence \u{*hex-digit-sequence*} is appended to *E*,
where *hex-digit-sequence* is the shortest hexadecimal representation
of *C* using lower-case hexadecimal digits[.](#format.string.escaped-2.2.1.2.sentence-2)

    +
          [(2.2.1.3)](#format.string.escaped-2.2.1.3)
          Otherwise, *C* is appended to *E*[.](#format.string.escaped-2.2.1.3.sentence-1)

  * [(2.2.2)](#format.string.escaped-2.2.2)

      Otherwise, if *X* is a shift sequence,
the effect on *E* and further decoding of *S* is unspecified[.](#format.string.escaped-2.2.2.sentence-1)
      *Recommended practice*: A shift sequence should be represented in *E* such that the original code unit sequence of *S* can be reconstructed[.](#format.string.escaped-2.2.2.sentence-2)

  * [(2.2.3)](#format.string.escaped-2.2.3)

      Otherwise (*X* is a sequence of ill-formed code units),
each code unit *U* is appended to *E* in order
as the sequence \x{*hex-digit-sequence*},
where *hex-digit-sequence* is the shortest hexadecimal representation of *U* using lower-case hexadecimal digits[.](#format.string.escaped-2.2.3.sentence-1)

- [(2.3)](#format.string.escaped-2.3)

  Finally, U+0022 quotation mark (")
is appended to *E*[.](#format.string.escaped-2.3.sentence-1)

Table [114](#tab:format.escape.sequences) — Mapping of characters to escape sequences [[tab:format.escape.sequences]](./tab:format.escape.sequences)  

| [ð](#tab:format.escape.sequences-row-1)<br>**Character** | **Escape sequence** |
| --- | --- |
| [ð](#tab:format.escape.sequences-row-2)<br>U+0009 character tabulation | \t |
| [ð](#tab:format.escape.sequences-row-3)<br>U+000a line feed | \n |
| [ð](#tab:format.escape.sequences-row-4)<br>U+000d carriage return | \r |
| [ð](#tab:format.escape.sequences-row-5)<br>U+0022 quotation mark | \" |
| [ð](#tab:format.escape.sequences-row-6)<br>U+005c reverse solidus | \\ |

[3](#format.string.escaped-3)

[#](http://github.com/Eelis/draft/tree/9adde4bc1c62ec234483e63ea3b70a59724c745a/source/text.tex#L7404)

The escaped string representation of a character *C* is equivalent to the escaped string representation
of a string of *C*, except that:

- [(3.1)](#format.string.escaped-3.1)

the result starts and ends with U+0027 apostrophe (')
instead of U+0022 quotation mark ("), and

- [(3.2)](#format.string.escaped-3.2)

if *C* is U+0027 apostrophe,
the two characters \' are appended to *E*, and

- [(3.3)](#format.string.escaped-3.3)

if *C* is U+0022 quotation mark,
then *C* is appended unchanged.

[*Example [1](#format.string.escaped-example-1)*: string s0 = format("[{}]", "h\tllo"); // s0 has value: [h llo] string s1 = format("[{:?}]", "h\tllo"); // s1 has value: ["h\tllo"] string s2 = format("[{:?}]", "Ð¡Ð¿Ð°ÑÐ¸Ð±Ð¾, ÐÐ¸ÐºÑÐ¾Ñ â¥!"); // s2 has value: ["Ð¡Ð¿Ð°ÑÐ¸Ð±Ð¾, ÐÐ¸ÐºÑÐ¾Ñ â¥!"] string s3 = format("[{:?}, {:?}]", '\'', '"'); // s3 has value: ['\'', '"']// The following examples assume use of the UTF-8 encoding string s4 = format("[{:?}]", string("\0 \n \t \x02 \x1b", 9)); // s4 has value: ["\u{0} \n \t \u{2} \u{1b}"] string s5 = format("[{:?}]", "\xc3\x28"); // invalid UTF-8, s5 has value: ["\x{c3}("] string s6 = format("[{:?}]", "ð¤·ð»ââï¸"); // s6 has value: ["ð¤·\u{200d}â"] string s7 = format("[{:?}]", "\u0301"); // s7 has value: ["\u{301}"] string s8 = format("[{:?}]", "\\\u0301"); // s8 has value: ["\\\u{301}"] string s9 = format("[{:?}]", "e\u0301\u0323"); // s9 has value: ["eÌÌ£"] â *end example*]

#### [28.5.6.6](#format.parse.ctx) Class template basic_format_parse_context [[format.parse.ctx]](format.parse.ctx)

[ð](#lib:basic_format_parse_context)

namespace std {template<class charT>class basic_format_parse_context {public:using char_type = charT; using const_iterator = typename basic_string_view<charT>::const_iterator; using iterator = const_iterator; private: iterator begin_; // *exposition only* iterator end_; // *exposition only*enum indexing { unknown, manual, automatic }; // *exposition only* indexing indexing_; // *exposition only* size_t next_arg_id_; // *exposition only* size_t num_args_; // *exposition only*public:constexpr explicit basic_format_parse_context(basic_string_view<charT> fmt) noexcept;
 basic_format_parse_context(const basic_format_parse_context&) = delete;
 basic_format_parse_context& operator=(const basic_format_parse_context&) = delete; constexpr const_iterator begin() const noexcept; constexpr const_iterator end() const noexcept; constexpr void advance_to(const_iterator it); constexpr size_t next_arg_id(); constexpr void check_arg_id(size_t id); template<class... Ts>constexpr void check_dynamic_spec(size_t id) noexcept; constexpr void check_dynamic_spec_integral(size_t id) noexcept; constexpr void check_dynamic_spec_string(size_t id) noexcept; };}

[1](#format.parse.ctx-1)

[#](http://github.com/Eelis/draft/tree/9adde4bc1c62ec234483e63ea3b70a59724c745a/source/text.tex#L7482)

An instance of basic_format_parse_context holds
the format string parsing state, consisting of
the format string range being parsed and
the argument counter for automatic indexing[.](#format.parse.ctx-1.sentence-1)

[2](#format.parse.ctx-2)

[#](http://github.com/Eelis/draft/tree/9adde4bc1c62ec234483e63ea3b70a59724c745a/source/text.tex#L7488)

If a program declares an explicit or partial specialization ofbasic_format_parse_context,
the program is ill-formed, no diagnostic required[.](#format.parse.ctx-2.sentence-1)

[ð](#lib:basic_format_parse_context,constructor)

`constexpr explicit basic_format_parse_context(basic_string_view<charT> fmt) noexcept;
`

[3](#format.parse.ctx-3)

[#](http://github.com/Eelis/draft/tree/9adde4bc1c62ec234483e63ea3b70a59724c745a/source/text.tex#L7499)

*Effects*: Initializesbegin_ with fmt.begin(),end_ with fmt.end(),indexing_ with unknown,next_arg_id_ with 0, andnum_args_ with 0[.](#format.parse.ctx-3.sentence-1)

[*Note [1](#format.parse.ctx-note-1)*:

Any call tonext_arg_id, check_arg_id, or check_dynamic_spec on an instance of basic_format_parse_context initialized using this constructor is not a core constant expression[.](#format.parse.ctx-3.sentence-2)

â *end note*]

[ð](#lib:begin,basic_format_parse_context)

`constexpr const_iterator begin() const noexcept;
`

[4](#format.parse.ctx-4)

[#](http://github.com/Eelis/draft/tree/9adde4bc1c62ec234483e63ea3b70a59724c745a/source/text.tex#L7521)

*Returns*: begin_[.](#format.parse.ctx-4.sentence-1)

[ð](#lib:end,basic_format_parse_context)

`constexpr const_iterator end() const noexcept;
`

[5](#format.parse.ctx-5)

[#](http://github.com/Eelis/draft/tree/9adde4bc1c62ec234483e63ea3b70a59724c745a/source/text.tex#L7532)

*Returns*: end_[.](#format.parse.ctx-5.sentence-1)

[ð](#lib:advance_to,basic_format_parse_context)

`constexpr void advance_to(const_iterator it);
`

[6](#format.parse.ctx-6)

[#](http://github.com/Eelis/draft/tree/9adde4bc1c62ec234483e63ea3b70a59724c745a/source/text.tex#L7543)

*Preconditions*: end() is reachable from it[.](#format.parse.ctx-6.sentence-1)

[7](#format.parse.ctx-7)

[#](http://github.com/Eelis/draft/tree/9adde4bc1c62ec234483e63ea3b70a59724c745a/source/text.tex#L7547)

*Effects*: Equivalent to: begin_ = it;

[ð](#lib:next_arg_id,basic_format_parse_context)

`constexpr size_t next_arg_id();
`

[8](#format.parse.ctx-8)

[#](http://github.com/Eelis/draft/tree/9adde4bc1c62ec234483e63ea3b70a59724c745a/source/text.tex#L7558)

*Effects*: If indexing_ != manual is true, equivalent to:if (indexing_ == unknown) indexing_ = automatic;return next_arg_id_++;

[9](#format.parse.ctx-9)

[#](http://github.com/Eelis/draft/tree/9adde4bc1c62ec234483e63ea3b70a59724c745a/source/text.tex#L7567)

*Throws*: format_error if indexing_ == manual is true[.](#format.parse.ctx-9.sentence-1)

[*Note [2](#format.parse.ctx-note-2)*:

This indicates mixing of automatic and manual argument indexing[.](#format.parse.ctx-9.sentence-2)

â *end note*]

[10](#format.parse.ctx-10)

[#](http://github.com/Eelis/draft/tree/9adde4bc1c62ec234483e63ea3b70a59724c745a/source/text.tex#L7574)

*Remarks*: Let *cur-arg-id* be the value of next_arg_id_ prior to this call[.](#format.parse.ctx-10.sentence-1)

Call expressions where *cur-arg-id* >= num_args_ is true are not core constant expressions ([[expr.const]](expr.const "7.7 Constant expressions"))[.](#format.parse.ctx-10.sentence-2)

[ð](#lib:check_arg_id,basic_format_parse_context)

`constexpr void check_arg_id(size_t id);
`

[11](#format.parse.ctx-11)

[#](http://github.com/Eelis/draft/tree/9adde4bc1c62ec234483e63ea3b70a59724c745a/source/text.tex#L7587)

*Effects*: If indexing_ != automatic is true, equivalent to:if (indexing_ == unknown) indexing_ = manual;

[12](#format.parse.ctx-12)

[#](http://github.com/Eelis/draft/tree/9adde4bc1c62ec234483e63ea3b70a59724c745a/source/text.tex#L7595)

*Throws*: format_error ifindexing_ == automatic is true[.](#format.parse.ctx-12.sentence-1)

[*Note [3](#format.parse.ctx-note-3)*:

This indicates mixing of automatic and manual argument indexing[.](#format.parse.ctx-12.sentence-2)

â *end note*]

[13](#format.parse.ctx-13)

[#](http://github.com/Eelis/draft/tree/9adde4bc1c62ec234483e63ea3b70a59724c745a/source/text.tex#L7603)

*Remarks*: A call to this function is a core constant expression ([[expr.const]](expr.const "7.7 Constant expressions")) only ifid < num_args_ is true[.](#format.parse.ctx-13.sentence-1)

[ð](#lib:check_dynamic_spec,basic_format_parse_context)

`template<class... Ts>
  constexpr void check_dynamic_spec(size_t id) noexcept;
`

[14](#format.parse.ctx-14)

[#](http://github.com/Eelis/draft/tree/9adde4bc1c62ec234483e63ea3b70a59724c745a/source/text.tex#L7616)

*Mandates*: sizeof...(Ts) â¥ 1[.](#format.parse.ctx-14.sentence-1)

The types in Ts... are unique[.](#format.parse.ctx-14.sentence-2)

Each type in Ts... is one ofbool,char_type,int,unsigned int,long long int,unsigned long long int,float,double,long double,const char_type*,basic_string_view<char_type>, orconst void*[.](#format.parse.ctx-14.sentence-3)

[15](#format.parse.ctx-15)

[#](http://github.com/Eelis/draft/tree/9adde4bc1c62ec234483e63ea3b70a59724c745a/source/text.tex#L7634)

*Remarks*: A call to this function is a core constant expression only if

- [(15.1)](#format.parse.ctx-15.1)

id < num_args_ is true and

- [(15.2)](#format.parse.ctx-15.2)

the type of the corresponding format argument
(after conversion to basic_format_arg<Context>) is one of the types in Ts...[.](#format.parse.ctx-15.sentence-1)

[ð](#lib:check_dynamic_spec_integral,basic_format_parse_context)

`constexpr void check_dynamic_spec_integral(size_t id) noexcept;
`

[16](#format.parse.ctx-16)

[#](http://github.com/Eelis/draft/tree/9adde4bc1c62ec234483e63ea3b70a59724c745a/source/text.tex#L7652)

*Effects*: Equivalent to:check_dynamic_spec<int, unsigned int, long long int, unsigned long long int>(id);

[ð](#lib:check_dynamic_spec_string,basic_format_parse_context)

`constexpr void check_dynamic_spec_string(size_t id) noexcept;
`

[17](#format.parse.ctx-17)

[#](http://github.com/Eelis/draft/tree/9adde4bc1c62ec234483e63ea3b70a59724c745a/source/text.tex#L7666)

*Effects*: Equivalent to:check_dynamic_spec<const char_type*, basic_string_view<char_type>>(id);

#### [28.5.6.7](#format.context) Class template basic_format_context [[format.context]](format.context)

[ð](#lib:basic_format_context)

namespace std {template<class Out, class charT>class basic_format_context { basic_format_args<basic_format_context> args_; // *exposition only* Out out_; // *exposition only* basic_format_context(const basic_format_context&) = delete;
 basic_format_context& operator=(const basic_format_context&) = delete; public:using iterator = Out; using char_type = charT; template<class T> using formatter_type = formatter<T, charT>;

 basic_format_arg<basic_format_context> arg(size_t id) const noexcept;
 std::locale locale();

 iterator out(); void advance_to(iterator it); };}

[1](#format.context-1)

[#](http://github.com/Eelis/draft/tree/9adde4bc1c62ec234483e63ea3b70a59724c745a/source/text.tex#L7704)

An instance of basic_format_context holds formatting state
consisting of the formatting arguments and the output iterator[.](#format.context-1.sentence-1)

[2](#format.context-2)

[#](http://github.com/Eelis/draft/tree/9adde4bc1c62ec234483e63ea3b70a59724c745a/source/text.tex#L7708)

If a program declares an explicit or partial specialization ofbasic_format_context,
the program is ill-formed, no diagnostic required[.](#format.context-2.sentence-1)

[3](#format.context-3)

[#](http://github.com/Eelis/draft/tree/9adde4bc1c62ec234483e63ea3b70a59724c745a/source/text.tex#L7713)

Out shall model [output_iterator](iterator.concept.output#concept:output_iterator "24.3.4.10 Concept output_­iterator [iterator.concept.output]")<const charT&>[.](#format.context-3.sentence-1)

[4](#format.context-4)

[#](http://github.com/Eelis/draft/tree/9adde4bc1c62ec234483e63ea3b70a59724c745a/source/text.tex#L7716)

format_context is an alias for
a specialization of basic_format_context with an output iterator
that appends to string,
such as back_insert_iterator<string>[.](#format.context-4.sentence-1)

Similarly, wformat_context is an alias for
a specialization of basic_format_context with an output iterator
that appends to wstring[.](#format.context-4.sentence-2)

[5](#format.context-5)

[#](http://github.com/Eelis/draft/tree/9adde4bc1c62ec234483e63ea3b70a59724c745a/source/text.tex#L7729)

*Recommended practice*: For a given type charT,
implementations should provide
a single instantiation of basic_format_context for appending tobasic_string<charT>,vector<charT>,
or any other container with contiguous storage
by wrapping those in temporary objects with a uniform interface
(such as a span<charT>) and polymorphic reallocation[.](#format.context-5.sentence-1)

[ð](#lib:arg,basic_format_context)

`basic_format_arg<basic_format_context> arg(size_t id) const noexcept;
`

[6](#format.context-6)

[#](http://github.com/Eelis/draft/tree/9adde4bc1c62ec234483e63ea3b70a59724c745a/source/text.tex#L7747)

*Returns*: args_.get(id)[.](#format.context-6.sentence-1)

[ð](#lib:locale,basic_format_context)

`std::locale locale();
`

[7](#format.context-7)

[#](http://github.com/Eelis/draft/tree/9adde4bc1c62ec234483e63ea3b70a59724c745a/source/text.tex#L7758)

*Returns*: The locale passed to the formatting function
if the latter takes one,
and std​::​locale() otherwise[.](#format.context-7.sentence-1)

[ð](#lib:out,basic_format_context)

`iterator out();
`

[8](#format.context-8)

[#](http://github.com/Eelis/draft/tree/9adde4bc1c62ec234483e63ea3b70a59724c745a/source/text.tex#L7771)

*Effects*: Equivalent to: return std​::​move(out_);

[ð](#lib:advance_to,basic_format_context)

`void advance_to(iterator it);
`

[9](#format.context-9)

[#](http://github.com/Eelis/draft/tree/9adde4bc1c62ec234483e63ea3b70a59724c745a/source/text.tex#L7782)

*Effects*: Equivalent to: out_ = std​::​move(it);

[*Example [1](#format.context-example-1)*: struct S { int value; };

template<> struct std::formatter<S> { size_t width_arg_id = 0; // Parses a width argument id in the format { *digit* }.constexpr auto parse(format_parse_context& ctx) {auto iter = ctx.begin(); auto is_digit = [](auto c) { return c >= '0' && c <= '9'; }; auto get_char = [&]() { return iter != ctx.end() ? *iter : 0; }; if (get_char() != '{')return iter; ++iter; char c = get_char(); if (!is_digit(c) || (++iter, get_char()) != '}')throw format_error("invalid format");
 width_arg_id = c - '0';
 ctx.check_arg_id(width_arg_id); return ++iter; }// Formats an S with width given by the argument width_arg_id.auto format(S s, format_context& ctx) const {int width = ctx.arg(width_arg_id).visit([](auto value) -> int {if constexpr (!is_integral_v<decltype(value)>)throw format_error("width is not integral"); else if (value < 0 || value > numeric_limits<int>::max())throw format_error("invalid width"); elsereturn value; }); return format_to(ctx.out(), "{0:x>{1}}", s.value, width); }};

std::string s = std::format("{0:{1}}", S{42}, 10); // value of s is "xxxxxxxx42" â *end example*]
