HN 日本語サマリー

← 一覧へ戻る
プログラミング

C++26のStd::Formatにおける改善点

Improvements to Std:Format in C++26 (mariusbancila.ro)

7 pointsby jandeboevrie1 コメント

要約

C++26標準では、formatライブラリに多くの改善が加えられています。主な変更点として、空行の直接出力、ポインタ型の直接フォーマット、`std::filesystem::path`のフォーマットが挙げられます。また、`std::format`関連関数が`constexpr`に対応し、実行時フォーマット文字列を扱う`std::dynamic_format`が導入されるなど、より柔軟で強力な文字列フォーマットが可能になります。

全文翻訳

C++26標準では、フォーマットライブラリに一連の改善が加えられています。この記事では、それらの中でも最も重要なものを見ていきましょう。\n\n**空行の出力**\nC++26以前は、空行を出力するには次のように記述する必要がありました: `std::print("\\n");`\nC++26では、`std::println`に引数なしのオーバーロードが追加され、コンソールに改行を出力します。\n`std::print();`\n\n**ポインタのフォーマット**\nポインタ型を直接フォーマットすることはできませんでした。ポインタ型を整数型として再解釈して出力するというハックが必要でした。\n```cpp\nint i = 0;\nconst void* p = &i;\nstd::println("{:#018x}", reinterpret_cast<uintptr_t>(p));\n```\nC++26では、フォーマットライブラリがポインタ型のフォーマットを直接サポートします:\n* 暗黙的に、指定子は不要\n* 明示的に、`p` (小文字) または `P` (大文字) 指定子を使用\nヌルポインタは `0x0` としてフォーマットされます(パディングが指定されていない場合)。\nポインタをフォーマットするいくつかの例を以下に示します:\n```cpp\nint i = 0;\nconst void* p = &i;\nstd::println("{}", p); // lowercase, the default => 0x7fffb2715a54\nstd::println("{:p}", p); // explicit, same as none => 0x7fffb2715a54\nstd::println("{:P}", p); // uppercase => 0X7FFFB2715A54\nstd::println("{:018}", p); // zero-padded to width 18 => 0x00007fffb2715a54\nstd::println("{:>20}", p); // right-aligned in a 20-wide field => 0x7fffb2715a54\nstd::println("{}", nullptr); // => 0x0\nstd::println("{:016}", nullptr); // => 0x00000000000000\n```\n\n**パスのフォーマット**\n回避策が必要だったもう一つの機能は、`std::filesystem`名前空間からのパスの出力でした。`path::string()`を使用してパスの文字列表現を取得できます。\n```cpp\nnamespace fs = std::filesystem;\nfs::path p = "/usr/local/bin/clang++";\nstd::println("{}", p.string());\n```\nしかし、これはパスを引用符なしで出力します。一方、`<<`演算子を使用すると、パスが引用符付きで出力されます。\n`std::cout << p << '\\n';`\nC++26では、`std::filesystem::path`用の`std::formatter`が追加され、パスのフォーマットが容易になります。\n* デフォルトでは、パスは引用符なしでフォーマットされます\n* `?`オプションは、エスケープされた表現(引用符付き)を与えるデバッグ形式を定義します\n* `g`オプションは、ジェネリック(スラッシュ)区切り文字を強制します(主にWindowsで現れます)\n```cpp\nfs::path p = "/usr/local/bin/clang++";\nstd::println("{}", p); // /usr/local/bin/clang++\nstd::println("{:?}", p); // "/usr/local/bin/clang++"\nfs::path w = R"(C:\\Users\\marius\\file.txt)";\nstd::println("{}", w); // C:\\Users\\marius\\file.txt (native separators)\nstd::println("{:g}", w); // C:/Users/marius/file.txt (generic)\nstd::println("{:g?}", w); // "C:/Users/marius/file.txt" (generic + escaped)\n```\n関連する問題として、Windowsのパスの文字列表現が解決されました。`std::filesystem::path`は、テキストをUTF-16としてエンコードされた`wchar_t`に格納します(Windowsネイティブ)。しかし、`p.string()`は、フォーマットライブラリが期待するUTF-8ではなく、アクティブなコードページに狭めます。その結果、非ASCIIパスが文字化けする可能性がありました。C++26の`std::formatter<std::filesystem::path>`は、WindowsネイティブのUTF-16をUnicodeトランスコーディングを使用してUTF-8に変換し、コードページを回避することで問題を解決します。不正な形式のUTF-16はデフォルトでU+FFFDに置き換えられるか、`{:?}`でエスケープされます。\n\n**constexpr std::format**\nC++26では、フォーマット関数`std::format`、`std::vformat`、`std::format_to`、`std::format_to_n`、`std::formatted_size`、およびそのワイドバリアント、さらに基礎となる部分(フォーマットコンテキスト、`std::basic_format_arg`、`std::basic_format_string`、および標準フォーマッタの`format`メンバー)が`constexpr`になります。これにより、例えば`std::format`で`static_assert`を使用することが可能になります。\n```cpp\nstatic_assert(std::format("{} {}", 1, 2) == "1 2");\nstatic_assert(sizeof(void*) == 8, std::format("expected 64-bit, pointer is {} bytes", sizeof(void*)));\n```\nこれは、`constexpr`化された`to_chars()`オーバーロード(ただし整数型のみ)に依存するため機能します。したがって、文字列、整数型、bool、char、ポインタで使用できます。しかし、この機能にはいくつかの制限があります。以下はサポートされていません:\n* 浮動小数点型\n* chrono型\n* ロケール対応のフォーマット(`L`指定子 – `{:L}`のように使用すると、呼び出しは非定数になります)\n今のところ、コンパイル時`std::format`は整数、文字列、診断によく対応しており、浮動小数点サポートは別の提案書(P3652)で浮動小数点`<charconv>`関数を`constexpr`にするのを待っています。\n\n**std::runtime_formatがstd::dynamic_formatになる**\n`std::format`または`std::print`のフォーマット文字列は定数式でなければなりません。例えば、次のように書けます:\n`std::println("{} = {}", "x", 13);`\nしかし、次のように書くことはできません:\n```cpp\nstd::string strf = "{} = {}";\nstd::println(strf, "x", 13);\n```\n`strf`は実行時にしか分からないため定数式ではなく、これは不正な形式です。回避策は`std::vformat`を使用することです:\n```cpp\nconst char* key = "y";\nint val = 13;\nstd::string strf = "{} = {}";\nstd::string s = std::vformat(strf, std::make_format_args(key, val));\nstd::println("{}", s);\n```\nこの回避策に対する回避策(基本的に`std::vformat`の糖衣構文)として、以前は`std::runtime_format`として知られていた関数があります。これは、ユーザー指向のフォーマット関数で直接使用でき、`std::basic_format_string`に暗黙的に変換できる動的なフォーマット文字列を格納するオブジェクトを返します。\n```cpp\nstd::string strf = "{} = {}";\nstd::println(std::runtime_format(strf), "x", 13);\n```\nC++26の最終バージョンでは、これは単に`std::dynamic_format`に名前が変更されました(ただし、この機能をサポートするコンパイラでは現時点でも以前の名前が使用されています)。したがって、上記のコードスニペットは次のようになります:\n```cpp\nstd::string strf = "{} = {}";\nstd::println(std::dynamic_format(strf), "x", 13);\n```\n`std::dynamic_format`は`constexpr`であるため、次の例のように定数評価コンテキストで使用できます:\n```cpp\nconstexpr auto make_string = [](std::string_view f) { return std::format(std::dynamic_format(f), 1, 2); };\nstatic_assert(make_string("{}+{}") == "1+2");\n```\n\n**詳細情報**\nこれらの変更については、以下の記事でさらに詳しく知ることができます:\n* C++26: std::format improvement (Part 1)\n* C++26: std::format improvements (Part 2)\n* constexpr std::format\n\nこれを共有する: Facebookで共有 (新しいウィンドウで開きます) Facebook Xで共有 (新しいウィンドウで開きます) X 印刷 (新しいウィンドウで開きます) 印刷 その他 友人にメールでリンクを送信 (新しいウィンドウで開きます) メール Redditで共有 (新しいウィンドウで開きます) Reddit\nいいね:いいね\n読み込み中...