C++教科書/標準ライブラリ編/<print>の章

編集

はじめに

編集

この章では、C++23で導入された新しい入出力ライブラリヘッダーである<print>について説明します。<print> ヘッダーは、フォーマット指定子を使用した柔軟な出力機能を提供します。

機能

編集

<print> ヘッダーは以下の主要な機能を提供します。

  • std::print 関数: フォーマット指定子と可変個引数を使用して、標準出力 (stdout) またはファイルストリームに出力します。
  • std::println 関数: std::print 関数と同様ですが、各出力の後に改行文字 (\n) を追加します。
  • std::vprint 関数群: タイプ情報を消去した引数表現を使用して、出力を行います。Unicode 対応の入出力ストリームとそうでないストリーム向けの関数があります。

関数の詳細

編集

この節では、各関数の詳細について説明します。

編集

std::print 関数は、最も基本的な出力関数です。フォーマット指定子と可変個の引数を受け取り、フォーマットに従って引数を整形した結果を出力先に送ります。出力先は、標準出力 (stdout) または FILE オブジェクトを指定できます。

構文

編集
template <class... Args>
void print(format_string<Args...> fmt, Args&&... args);

template <class... Args>
void print(FILE* stream, format_string<Args...> fmt, Args&&... args);

引数

編集
  • fmt: フォーマット指定子オブジェクト。std::format ヘッダーで定義された型である必要があります。
  • args: 可変個の引数。フォーマット指定子で指定されたプレースホルダーと一致する型である必要があります。
  • stream: 出力先となる FILE オブジェクト。省略すると、標準出力 (stdout) になります。

戻り値

編集

なし

#include <print>

int main() {
    int age = 30;
    std::string name = "太郎";
    std::print("名前: ", name, ", 年齢: ", age, "\n");
    return 0;
}

println 関数

編集

std::println 関数は、std::print 関数と同様ですが、出力の最後に改行文字 (\n) を追加します。単独で呼び出すこともでき、この場合、空の文字列が出力されます。

構文

編集
template <class... Args>
void println(format_string<Args...> fmt, Args&&... args);

void println();

template <class... Args>
void println(FILE* stream, format_string<Args...> fmt, Args&&... args);

void println(FILE* stream);

引数

編集
  • fmt: フォーマット指定子オブジェクト。std::format ヘッダーで定義された型である必要があります。省略すると、空の文字列になります。
  • args: 可変個の引数。フォーマット指定子で指定されたプレースホルダーと一致する型である必要があります。省略すると、引数は渡されません。
  • stream: 出力先となる FILE オブジェクト。省略すると、標準出力 (stdout) になります。

戻り値

編集

なし

#include <print>

int main() {
    std::println("Hello, World!");
    std::println("年齢は", 30, "歳です。");
    return 0;
}

vprint 関数群

編集

std::vprint 関数群は、可変個引数を受け取り、その型の情報を消去してから出力を行います。これにより、テンプレート化を簡略化したり、可変長の引数パックを処理しやすくなります。Unicode 対応の入出力ストリーム向けの vprint_unicode 関数と、そうでないストリーム向けの vprint_nonunicode 関数が用意されています。どちらの関数も、フォーマット文字列と可変長引数パックを受け取ります。

概要

編集

std::vprint 関数群で使用されるフォーマット文字列は、std::format ヘッダーで定義された string_view 型の文字列です。この文字列には、出力するデータの形式を指定するためのプレースホルダーが含まれます。プレースホルダーは、{} などのブレイスタイプ記号で囲まれます。

プレースホルダーの種類

編集

std::vprint 関数群で使用できるプレースホルダーには、以下の種類があります。

  • 整数プレースホルダー: {}
  • 浮動小数点プレースホルダー: {:.nf}
  • 文字列プレースホルダー: {:[flags][width][.precision]}s
  • ポインタプレースホルダー: {:[flags][width][.precision]}p
  • カスタムフォーマタープレースホルダー: {:[flags][width][.precision]}fmt

プレースホルダーの詳細

編集
整数プレースホルダー
{} プレースホルダーは、整数値を出力します。
std::vprint_unicode("数値: {}\n", std::make_format_args(100));
出力結果
数値: 100
浮動小数点プレースホルダー
{:.nf} プレースホルダーは、浮動小数点値を出力します。n は精度を指定する整数で、. の後に指定します。
std::vprint_unicode("小数: {:.2f}\n", std::make_format_args(3.14159265));
出力結果
小数: 3.14
文字列プレースホルダー
{:[flags][width][.precision]}s プレースホルダーは、文字列を出力します。
  • [flags]: 出力形式を設定するフラグ。省略できます。
    • -: 左寄せ
    • +: 正号を常に表示
    • : 空白を常に表示
  • [width]: 出力幅を設定する整数。省略できます。
  • [.precision]: 出力する文字数または有効桁数を設定する整数。省略できます。
std::string name = "太郎";
std::vprint_unicode("名前: {:-10}さん\n", std::make_format_args(name));
出力結果
名前: 太郎       さん
ポインタプレースホルダー
{:[flags][width][.precision]}p プレースホルダーは、ポインタ値を出力します。
* [flags]: 出力形式を設定するフラグ。省略できます。
** #: 0x を接頭辞として付加
* [width]: 出力幅を設定する整数。省略できます。
* [.precision]: 出力する桁数を設定する整数。省略できます。
int num = 100;
std::vprint_unicode("ポインタ: {#:x}\n", std::make_format_args(&num));
出力結果
ポインタ: 0x64
カスタムフォーマタープレースホルダー
{:[flags][width][.precision]}fmt プレースホルダーは、カスタムフォーマターを使用してデータを出力します。
  • [flags]: 出力形式を設定するフラグ。省略できます。
    • -: 左寄せ
    • +: 正号を常に表示
    • : 空白を常に表示
  • [width]: 出力幅を設定する整数。省略できます。
  • [.precision]: 出力する文字数または有効桁数を設定する整数。省略できます。
  • fmt: カスタムフォーマターを指定する文字列。
struct Person {
   std::string name;
   int age;
 };
 
 Person p{"太郎", 30};
 std::vprint_unicode("名前: {}, 年齢: {}\n", std::make_format_args(p, [](const Person& p) {
   return std::make_tuple(p.name, p.age);
 }));
出力結果
名前: 太郎, 年齢: 30
まとめ
std::vprint 関数群で使用されるフォーマット文字列は、出力するデータの形式を柔軟に指定することができます。

vprint_unicode 関数群

編集
構文
void vprint_unicode(string_view fmt, format_args args);
void vprint_unicode(FILE* stream, string_view fmt, format_args args);
void vprint_unicode_locking(FILE* stream, string_view fmt, format_args args);
引数
  • fmt: フォーマット文字列。std::format ヘッダーで定義された string_view 型である必要があります。
  • args: 可変長引数パック。std::format ヘッダーで定義された format_args 型である必要があります。
  • stream: 出力先となる FILE オブジェクト。省略すると、標準出力 (stdout) になります。
戻り値
なし
#include <print>

int main() {
    std::string_view fmt = "名前: {}, 年齢: {}\n";
    std::string name = "太郎";
    int age = 30;
    std::vprint_unicode(fmt, std::make_format_args(name, age));
    return 0;
}

vprint_nonunicode 関数群

編集
構文
void vprint_nonunicode(string_view fmt, format_args args);
void vprint_nonunicode(FILE* stream, string_view fmt, format_args args);
void vprint_nonunicode_locking(FILE* stream, string_view fmt, format_args args);
引数
  • fmt: フォーマット文字列。std::format ヘッダーで定義された string_view 型である必要があります。
  • args: 可変長引数パック。std::format ヘッダーで定義された format_args 型である必要があります。
  • stream: 出力先となる FILE オブジェクト。省略すると、標準出力 (stdout) になります。
戻り値
なし
#include <print>

int main() {
    std::string_view fmt = "名前: {}, 年齢: {}\n";
    std::string name = "太郎";
    int age = 30;
    std::vprint_nonunicode(fmt, std::make_format_args(name, age));
    return 0;
}
<print>は他の言語からの移行への配慮?
C++23における<print>ヘッダーの導入は、C++の使いやすさを向上させ、他の言語からの移行を容易にするための一環として考えられています。特に、PythonやRustなどのモダンなプログラミング言語で見られるシンプルで強力なフォーマット機能に触発されています。

以下に、その具体的な背景と利点について説明します。

背景
他の言語と比較すると、C++の標準ライブラリで提供される出力機能(std::coutなど)は強力ですが、やや冗長で使いにくい部分がありました。例えば、複数の変数を一行で出力する場合、std::coutを使用すると以下のように書く必要があります。
#include <iostream>

int main() {
    int x = 10;
    double y = 20.5;
    std::cout << "The value of x is " << x << " and the value of y is " << y << std::endl;
    return 0;
}
これに対し、Pythonのような言語ではフォーマット文字列を使用することでより簡潔に書くことができます。
x = 10
y = 20.5
print(f"The value of x is {x} and the value of y is {y}")
この違いを埋めるために、C++23では<print>ヘッダーが導入されました。
<print>の利点
簡潔さと読みやすさ
    #include <print>

    int main() {
        int x = 10;
        double y = 20.5;
        std::print("The value of x is {} and the value of y is {}\n", x, y);
        return 0;
    }
フォーマットされた文字列を使用することで、コードが簡潔になり、読みやすさが向上します。
他言語との類似性
C++23の<print>は、Pythonのprint関数やRustのprintln!マクロと似た構文を採用しており、これらの言語からC++への移行をスムーズにします。
Python
    print(f"The value of x is {x} and the value of y is {y}")
Rust
    println!("The value of x is {} and the value of y is {}", x, y);
統一されたインターフェース
従来のstd::coutに比べて、std::printは一貫性のあるインターフェースを提供し、エラーが少なく、直感的に使いやすいです。
フォーマットの柔軟性
変数のフォーマット方法を簡単にカスタマイズできます。例えば、小数点以下の桁数を指定するなどの操作が容易です。
結論
C++23の<print>ヘッダーは、他のモダンなプログラミング言語からの移行を容易にし、C++の使いやすさを向上させるための重要なステップです。これにより、C++の学習曲線が緩和され、他の言語からの移行者や新規学習者にとって魅力的な選択肢となるでしょう。


まとめ

編集

<print> ヘッダーは、フォーマット指定子を使用した柔軟な出力機能を提供します。std::print 関数、std::println 関数、std::vprint 関数群の使い分けを理解することで、様々な出力形式に対応することができます。

参考文献

編集