20 General utilities library [utilities]

20.20 Formatting [format]

20.20.6 Formatter [format.formatter]

20.20.6.1 Formatter requirements [formatter.requirements]

A type F meets the BasicFormatter requirements if:
A type F meets the Formatter requirements if it meets the BasicFormatter requirements and the expressions shown in Table 71 are valid and have the indicated semantics.
Given character type charT, output iterator type Out, and formatting argument type T, in Table 70 and Table 71:
  • f is a value 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<charT>,
  • 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 the format-spec ([format.string]) of the replacement field being formatted in the format string.
If format-spec is empty then either pc.begin() == pc.end() or *pc.begin() == '}'.
Table 70: BasicFormatter requirements [tab:formatter.basic]
Expression
Return type
Requirement
f.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 71: Formatter 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.

20.20.6.2 Formatter specializations [format.formatter.spec]

The functions defined in [format.functions] use specializations of the class template formatter to format individual arguments.
Let charT be either char or wchar_­t.
Each specialization of formatter is either enabled or disabled, as described below.
Each header that declares the template formatter provides the following enabled specializations:
  • The specializations template<> struct formatter<char, char>; template<> struct formatter<char, wchar_t>; template<> struct formatter<wchar_t, wchar_t>;
  • For each charT, the string type specializations template<> struct formatter<charT*, charT>; template<> struct formatter<const charT*, charT>; template<size_t N> struct formatter<const 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>;
  • For each charT, for each cv-unqualified arithmetic type ArithmeticT other than char, wchar_­t, char8_­t, char16_­t, or char32_­t, a specialization template<> struct formatter<ArithmeticT, charT>;
  • For each charT, the pointer type specializations template<> 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].
[Note 1:
Specializations such as formatter<wchar_­t, char> and formatter<const char*, wchar_­t> that would require implicit multibyte / wide string or character conversion are disabled.
— end note]
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.
If the library provides an explicit or partial specialization of formatter<T, charT>, that specialization is enabled and meets the Formatter requirements except as noted otherwise.
If F is a disabled specialization of formatter, these values are false:
  • is_­default_­constructible_­v<F>,
  • is_­copy_­constructible_­v<F>,
  • is_­move_­constructible_­v<F>,
  • is_­copy_­assignable_­v<F>, and
  • is_­move_­assignable_­v<F>.
An enabled specialization formatter<T, charT> meets the BasicFormatter requirements ([formatter.requirements]).
[Example 1: #include <format> 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) { 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]

20.20.6.3 Class template basic_­format_­parse_­context [format.parse.ctx]

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, size_t num_args = 0) 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); }; }
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.
constexpr explicit basic_format_parse_context(basic_string_view<charT> fmt, size_t num_args = 0) noexcept;
Effects: Initializes begin_­ with fmt.begin(), end_­ with fmt.end(), indexing_­ with unknown, next_­arg_­id_­ with 0, and num_­args_­ with num_­args.
constexpr const_iterator begin() const noexcept;
Returns: begin_­.
constexpr const_iterator end() const noexcept;
Returns: end_­.
constexpr void advance_to(const_iterator it);
Preconditions: end() is reachable from it.
Effects: Equivalent to: begin_­ = it;
constexpr size_t next_arg_id();
Effects: If indexing_­ != manual, equivalent to: if (indexing_ == unknown) indexing_ = automatic; return next_arg_id_++;
Throws: format_­error if indexing_­ == manual which indicates mixing of automatic and manual argument indexing.
constexpr void check_arg_id(size_t id);
Effects: If indexing_­ != automatic, equivalent to: if (indexing_ == unknown) indexing_ = manual;
Throws: format_­error if indexing_­ == automatic which indicates mixing of automatic and manual argument indexing.
Remarks: Call expressions where id >= num_­args_­ are not core constant expressions ([expr.const]).

20.20.6.4 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 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; std::locale locale(); iterator out(); void advance_to(iterator it); }; }
An instance of basic_­format_­context holds formatting state consisting of the formatting arguments and the output iterator.
Out shall model output_­iterator<const charT&>.
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>.
Similarly, wformat_­context is an alias for a specialization of basic_­format_­context with an output iterator that appends to wstring.
Recommended practice: For a given type charT, implementations should provide a single instantiation of basic_­format_­context for appending to basic_­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.
basic_format_arg<basic_format_context> arg(size_t id) const;
Returns: args_­.get(id).
std::locale locale();
Returns: The locale passed to the formatting function if the latter takes one, and std​::​locale() otherwise.
iterator out();
Effects: Equivalent to: return std​::​move(out_­);
void advance_to(iterator it);
Effects: Equivalent to: out_­ = std​::​move(it);
[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 get_char = [&]() { return iter != ctx.end() ? *iter : 0; }; if (get_char() != '{') return iter; ++iter; char c = get_char(); if (!isdigit(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) { int width = visit_format_arg([](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"); else return value; }, ctx.arg(width_arg_id)); 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]