最終更新日時(UTC):
が更新

履歴 編集

class template
<format>

std::formatter(C++20)

namespace std {
  template <class T, class charT = char>
  struct formatter;                                          // (1) C++20

  template <ranges::input_range R, class charT>
    requires (format_kind<R> != range_format::disabled) &&
             formattable<ranges::range_reference_t<R>, charT>
  struct formatter<R, charT>
    : range-default-formatter<format_kind<R>, R, charT> { }; // (2) C++23

  template <class charT, formattable<charT>... Ts>
  struct formatter<pair-or-tuple<Ts...>, charT>;             // (3) C++23

  template <class charT,
            class T,
            formattable<charT> Container,
            class... U>
  struct formatter<adaptor-type<T, Container, U...>, charT>; // (4) C++23
}

概要

フォーマット引数の個々の型に対応する書式文字列の解析と値のフォーマットを担うクラス。

(1)は、charTcharまたはwchar_tとすると、標準で以下の特殊化が利用できる。

  • template<> struct formatter<charT, charT>
  • template<> struct formatter<char, wchar_t>
  • 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>
  • 第1テンプレート引数がnullptr_t, void*, const void*, bool, すべてのCV修飾されない標準の整数型, 拡張整数型, 浮動小数点数型であり、第2テンプレート引数がcharTであるもの。

さらに、ユーザーがformatterを特殊化した場合、それも有効である。

標準でもユーザー定義でも特殊化されない場合、その型に対するformatterは無効であり、そのような型はフォーマット関数の引数にできない。

ワイド文字列とマルチバイト文字列を相互に変換するような特殊化は意図的に用意されていないが、ユーザーが用意することは禁止していない。

ユーザーの型でformatterを特殊化する場合の要件

formatterの有効な特殊化はFormatter要件を満たす必要がある。

FがFormatter要件を満たすとは、次のことをいう。

  • FCpp17DefaultConstructibleCpp17CopyConstructibleCpp17CopyAssignableCpp17Destructibleであること

さらに、以下の条件を満たすこと

  1. f.parse(pc) が有効であり、
    • 戻り値の型がPC::iteratorである
    • イテレータ範囲[pc.begin(), pc.end())を解析してformat_errorを投げるか、解析が終わった位置を指すイテレータを返す
  2. cf.format(t, fc) が有効であり、
    • 戻り値の型がFC::iteratorである
    • フォーマット結果をfc.out()へ出力し、出力後のイテレータを返す
    • 出力はtfc.locale()、最後に呼び出されたf.parse(pc)イテレータ範囲[pc.begin(), pc.end())以外に依存しない
  3. cf.format(u, fc) が有効であり、
    • 戻り値がFC::iteratorである
    • フォーマット結果をfc.out()へ出力し、出力後のイテレータを返す
    • 出力はufc.locale()、最後に呼び出されたf.parse(pc)イテレータ範囲[pc.begin(), pc.end())以外に依存しない
    • uを変更しない

条件内の各要素を、以下のように定義する。

  • 文字の型をcharT
  • 出力イテレータの型をOut
  • フォーマット引数の型をT
  • fFのオブジェクト
  • cfFconstオブジェクト
  • uTのlvalue
  • tTまたはconst Tへ変換できる型のオブジェクト
  • PCbasic_format_parse_context<charT>
  • FCbasic_format_context<Out, charT>
  • pcPCのlvalue
  • fcFCのlvalue

ただし、parseの呼び出し前の状態で、pc.begin()は書式文字列中の対応する置換フィールドのオプションの先頭を指す。

  • オプションが空でなければ、*pc.begin():の次の文字
  • オプションが空なら、pc.begin() == pc.end()または*pc.begin() == '}'である

std::formattableコンセプトも参照。

フォーマッターは、書式文字列中に置換フィールドが見つかるたびに次のコードと近い形で呼び出される。

typename FC::template formatter_type<T> f;
pc.advance_to(f.parse(pc));      // オプションを解析し状態を保存する
fc.advance_to(f.format(u, fc));  // 状態をもとにフォーマットを行う

handleも参照。

メンバ関数

メンバ関数 説明 対応バージョン
parse 書式の解析を行う C++20
format 書式化を行う C++20

文字・文字列に対する特殊化

メンバ関数 説明 対応バージョン
set_debug_format デバッグ出力を有効にする C++23

pair / tuple向けの特殊化

メンバ関数 説明 対応バージョン
set_separator 要素の区切り文字を設定する C++23
set_brackets 全体の囲み文字を設定する C++23

オリジナル書式なし、型変換のみの場合

#include <iostream>
#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, std::format_context& ctx) const {
    return std::formatter<const char*>::format(color_names[c], ctx);
  }
};

int main()
{
  std::cout << std::format("{}", red) << std::endl;
}

出力

red

オリジナル書式を定義する例

#include <iostream>
#include <format>

enum color { red, green, blue };

const char* color_names[] = { "red", "green", "blue" };
const char* jp_color_names[] = { "赤", "緑", "青" };

template<>
struct std::formatter<color> {
  bool is_jp = false;

  // コンパイル時の書式文字列の解析があるため、
  // constexprにする必要がある。
  // この関数に渡されるパラメータは、{:%j}の%以降。
  // 解析がおわった場所を指すイテレータを返す。
  constexpr auto parse(std::format_parse_context& ctx) {
    auto it = ctx.begin();
    if (*it == '%') {
      ++it;
      if (*it == 'j') {
        is_jp = true;
      }
      else if (*it == 'e') {
        is_jp = false;
      }
      ++it;
    }
    return it;
  }

  // format()関数は書式の情報をもたない。
  // parse()関数で解析した書式をメンバ変数で保持しておいて、
  // それをもとに書式化する
  auto format(color c, std::format_context& ctx) const {
    return std::format_to(ctx.out(), "{}",
      is_jp ? jp_color_names[c] : color_names[c]
    );
  }
};

int main()
{
  std::cout << std::format("{:%j} {:%e}", red, blue) << std::endl;
}

出力

赤 blue

複数のメンバ変数を含むクラスの場合

#include <iostream>
#include <format>

struct Point {
  float x, y;
};

template<>
struct std::formatter<Point> : std::formatter<std::string> {
  auto format(Point p, std::format_context& ctx) const {
    return std::formatter<std::string>::format(
      std::format("[{}, {}]", p.x, p.y),
      ctx);
  }
};

int main()
{
  std::cout << std::format("{}", Point{1.2f, 3.4f}) << std::endl;
}

出力

[1.2, 3.4]

バージョン

言語

  • C++20

処理系

関連項目

参照