Files
2025-10-25 03:02:53 +03:00

707 lines
33 KiB
Markdown
Raw Permalink Blame History

This file contains invisible Unicode characters

This file contains invisible Unicode characters that are indistinguishable to humans but may be processed differently by a computer. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

[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.3Swappable 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.2Format 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.2Format 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.1Namespace 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.7Constant 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.10Concept output_­iterator[iterator.concept.output]")<const charT&> ([[iterator.concept.output]](iterator.concept.output "24.3.4.10Concept 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.3Concept formattable[format.formattable]") = // *exposition only*[semiregular](concepts.object#concept:semiregular "18.6Object 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.2Concept same_­as[concept.same]")<typename decltype(pc)::iterator>; { cf.format(t, fc) } -> [same_as](concept.same#concept:same_as "18.4.2Concept same_­as[concept.same]")<typename Context::iterator>; };
template<class T, class charT>concept [formattable](#concept:formattable "28.5.6.3Concept formattable[format.formattable]") =[*formattable-with*](#concept:formattable-with "28.5.6.3Concept 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.3Concept formattable[format.formattable]") if formatter<remove_cvref_t<T>, charT> meets
the *BasicFormatter* requirements ([[formatter.requirements]](#formatter.requirements "28.5.6.1Formatter 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.5Formatting 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.4Formatter 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.2Standard 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.1Formatter 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.5Formatting 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: ["ẹ́"] — *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.7Constant 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.7Constant 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.10Concept 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*]