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

33 KiB
Raw Permalink Blame History

[format.formatter]

28 Text processing library [text]

28.5 Formatting [format]

28.5.6 Formatter [format.formatter]

28.5.6.1 Formatter requirements [formatter.requirements]

1

#

A type F meets the BasicFormatter requirements if it meets the

Cpp17DefaultConstructible (Table 30),

Cpp17CopyConstructible (Table 32),

Cpp17CopyAssignable (Table 34),

Cpp17Swappable ([swappable.requirements]), and

Cpp17Destructible (Table 35)

requirements, and the expressions shown in Table 112 are valid and have the indicated semantics.

2

#

A type F meets the Formatter requirements if it meets the BasicFormatter requirements and the expressions shown in Table 113 are valid and have the indicated semantics.

3

#

Given character type charT, output iterator typeOut, and formatting argument type T, in Table 112 and Table 113:

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

g is an lvalue of type F,

u is an lvalue of type T,

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

PC is basic_format_parse_context,

FC is basic_format_context<Out, charT>,

pc is an lvalue of type PC, and

fc is an lvalue of type FC.

pc.begin() points to the beginning of theformat-spec ([format.string]) of the replacement field being formatted in the format string.

If format-spec is not present or empty then eitherpc.begin() == pc.end() or*pc.begin() == '}'.

Table 112BasicFormatter requirements [tab:formatter.basic]

🔗
Expression
Return type Requirement
🔗
g.parse(pc)
PC::iterator Parses format-spec ([format.string]) for type T in the range [pc.begin(), pc.end()) until the first unmatched character.
Throws format_error unless the whole range is parsed or the unmatched character is }.
[Note 1:
This allows formatters to emit meaningful error messages. — end note]
Stores the parsed format specifiers in *this and returns an iterator past the end of the parsed range.
🔗
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.
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).

Table 113Formatter requirements [tab:formatter]

🔗
Expression
Return type Requirement
🔗
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.
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).
🔗
f.format(u, fc)
FC::iterator As above, but does not modify u.

28.5.6.2 Formatter locking [format.formatter.locking]

🔗

template<class T> constexpr bool enable_nonlocking_formatter_optimization = false;

1

#

Remarks: Pursuant to [namespace.std], users may specialize enable_nonlocking_formatter_optimization for cv-unqualified program-defined types.

Such specializations shall be usable in constant expressions ([expr.const]) and have type const bool.

28.5.6.3 Concept formattable [format.formattable]

1

#

Let fmt-iter-for be an unspecified type that modelsoutput_iterator<const charT&> ([iterator.concept.output]).

template<class T, class Context, class Formatter = typename Context::template formatter_type<remove_const_t>>concept formattable-with = // exposition onlysemiregular &&requires(Formatter& f, const Formatter& cf, T&& t, Context fc, basic_format_parse_context pc){{ f.parse(pc) } -> same_as<typename decltype(pc)::iterator>; { cf.format(t, fc) } -> same_as; };

template<class T, class charT>concept formattable =formattable-with<remove_reference_t, basic_format_context<fmt-iter-for, charT>>;

2

#

A type T and a character type charT model formattable if formatter<remove_cvref_t, charT> meets the BasicFormatter requirements ([formatter.requirements]) and, if remove_reference_t is const-qualified, the Formatter requirements.

28.5.6.4 Formatter specializations [format.formatter.spec]

1

#

The functions defined in [format.functions] use specializations of the class template formatter to format individual arguments.

2

#

Let charT be either char or wchar_t.

Each specialization of formatter is either enabled or disabled, as described below.

A debug-enabled 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 ?.

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

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

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>;templatestruct formatter<basic_string_view<charT, traits>, charT>;

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>;

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

3

#

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 = true;

[Note 1:

Specializations such as formatter<wchar_t, char> that would require implicit multibyte / wide string or character conversion are disabled.

— end note]

4

#

The header provides the following disabled specializations:

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>;templatestruct formatter<basic_string_view<char, traits>, wchar_t>;

5

#

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.

6

#

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.

7

#

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

is_default_constructible_v,

is_copy_constructible_v,

is_move_constructible_v,

is_copy_assignable_v, and

is_move_assignable_v.

8

#

An enabled specialization formatter<T, charT> meets theBasicFormatter requirements ([formatter.requirements]).

[Example 1: #include #include enum color { red, green, blue };const char* color_names[] = { "red", "green", "blue" };

template<> struct std::formatter : 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 Formatting escaped characters and strings [format.string.escaped]

1

#

A character or string can be formatted as escaped to make it more suitable for debugging or for logging.

2

#

The escaped string E representation of a string S is constructed by encoding a sequence of characters as follows.

The associated character encoding CE for charT (Table 12) is used to both interpret S and construct E.

  • (2.1)

    U+0022 quotation mark (") is appended to E.

  • (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)

      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.

+
      [(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 andC 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 andC corresponds to a Unicode scalar value with the Unicode property Grapheme_Extend=Yes as described by UAX #44 of the Unicode Standard andC 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 andC 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.

+
      [(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)

    Otherwise, if X is a shift sequence, the effect on E and further decoding of S is unspecified. Recommended practice: A shift sequence should be represented in E such that the original code unit sequence of S can be reconstructed.

  • (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.

  • (2.3)

    Finally, U+0022 quotation mark (") is appended to E.

Table 114 — Mapping of characters to escape sequences [tab:format.escape.sequences]

🔗
Character
Escape sequence
🔗
U+0009 character tabulation
\t
🔗
U+000a line feed
\n
🔗
U+000d carriage return
\r
🔗
U+0022 quotation mark
"
🔗
U+005c reverse solidus
\

3

#

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

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

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

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

[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 Class template basic_format_parse_context [format.parse.ctx]

🔗

namespace std {templateclass basic_format_parse_context {public:using char_type = charT; using const_iterator = typename basic_string_view::const_iterator; using iterator = const_iterator; private: iterator begin_; // exposition only iterator end_; // exposition onlyenum indexing { unknown, manual, automatic }; // exposition only indexing indexing_; // exposition only size_t next_arg_id_; // exposition only size_t num_args_; // exposition onlypublic:constexpr explicit basic_format_parse_context(basic_string_view 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

#

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.

2

#

If a program declares an explicit or partial specialization ofbasic_format_parse_context, the program is ill-formed, no diagnostic required.

🔗

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

3

#

Effects: Initializesbegin_ with fmt.begin(),end_ with fmt.end(),indexing_ with unknown,next_arg_id_ with 0, andnum_args_ with 0.

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

— end note]

🔗

constexpr const_iterator begin() const noexcept;

4

#

Returns: begin_.

🔗

constexpr const_iterator end() const noexcept;

5

#

Returns: end_.

🔗

constexpr void advance_to(const_iterator it);

6

#

Preconditions: end() is reachable from it.

7

#

Effects: Equivalent to: begin_ = it;

🔗

constexpr size_t next_arg_id();

8

#

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

9

#

Throws: format_error if indexing_ == manual is true.

[Note 2:

This indicates mixing of automatic and manual argument indexing.

— end note]

10

#

Remarks: Let cur-arg-id be the value of next_arg_id_ prior to this call.

Call expressions where cur-arg-id >= num_args_ is true are not core constant expressions ([expr.const]).

🔗

constexpr void check_arg_id(size_t id);

11

#

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

12

#

Throws: format_error ifindexing_ == automatic is true.

[Note 3:

This indicates mixing of automatic and manual argument indexing.

— end note]

13

#

Remarks: A call to this function is a core constant expression ([expr.const]) only ifid < num_args_ is true.

🔗

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

14

#

Mandates: sizeof...(Ts) ≥ 1.

The types in Ts... are unique.

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

15

#

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

id < num_args_ is true and

the type of the corresponding format argument (after conversion to basic_format_arg) is one of the types in Ts....

🔗

constexpr void check_dynamic_spec_integral(size_t id) noexcept;

16

#

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

🔗

constexpr void check_dynamic_spec_string(size_t id) noexcept;

17

#

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

28.5.6.7 Class template basic_format_context [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 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

#

An instance of basic_format_context holds formatting state consisting of the formatting arguments and the output iterator.

2

#

If a program declares an explicit or partial specialization ofbasic_format_context, the program is ill-formed, no diagnostic required.

3

#

Out shall model output_iterator<const charT&>.

4

#

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.

Similarly, wformat_context is an alias for a specialization of basic_format_context with an output iterator that appends to wstring.

5

#

Recommended practice: For a given type charT, implementations should provide a single instantiation of basic_format_context for appending tobasic_string,vector, or any other container with contiguous storage by wrapping those in temporary objects with a uniform interface (such as a span) and polymorphic reallocation.

🔗

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

6

#

Returns: args_.get(id).

🔗

std::locale locale();

7

#

Returns: The locale passed to the formatting function if the latter takes one, and std::locale() otherwise.

🔗

iterator out();

8

#

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

🔗

void advance_to(iterator it);

9

#

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

[Example 1: struct S { int value; };

template<> struct std::formatter { 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::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]