はじめに
C言語から派生したオブジェクト指向プログラミング言語であるC++は、21世紀に入ってまったく別物とも言えるプログラミング言語に成長していきました。それは、Modern C++と称されています。1990年代にC++を触っていたプログラマが現在の仕様を知れば、隔世感に苛まれるのではないでしょうか。本連載では、かつてはC++をたしなんでいたという方、今からC++言語を始めるという方に向けて、Modern C++らしい言語仕様をピックアップし紹介していくことで、今のC++言語の姿を理解していただきます。
対象読者
- かつてはC++をたしなんでいたという方
- 今からC++言語を始めるという方
- モダンなプログラミング言語のパラダイムに興味のある方
必要な環境
本記事のサンプルコードは、以下の環境で動作を確認しています。なお、一部のサンプルは以下の環境では動作しないか、あるいは実験的実装(Experimental)なので動作が不安定になる可能性があります。
-
macOS Sonoma/Windows 11
- Xcode Command Line Tools 15.3.0(Clang 15.0.0)
- MingW-W64-builds 13.2.0(GCC 11.0.0)
[NOTE]主要コンパイラのC++対応状況
C++はC++ 20まで標準化されていますが(ISO/IEC 14882:2020)、コンパイラが全ての仕様をサポートできているわけではありません。Clang 15、GCC 11ともにC++ 17まではフル実装ですが、C++ 20以降の仕様に関しては未実装か、実験的実装です。最新の対応状況は、以下のページで確認できます。
[NOTE]C++プログラムのコンパイル
以降のサンプルは、基本的に「gcc -o 出力先ファイル名 ソースファイル名」でコンパイルできますが、環境によってはライブラリやC++バージョンの指定が必要になることがあります。コンパイルやリンクでエラーが発生するときには、以下のようにライブラリの指定(-lstdc++)、C++バージョンの指定(-std=c++17など)の追加を検討してください。
% gcc -o sample sample.cpp -lstdc++ -std=c++17 // c++14, c++17, c++20など
書式付き文字列std::format[C++20]
C言語からプログラミングをはじめると、書式付き文字列出力といえばprintfとかfprintfとかsprintfだったんですが、C++になったらストリームを使え! と言われて驚いたものでした。「cout <<」とか書いて、出力するものをずらずら書いていく、あれです。「<<」の意味が不明でしたし、いろいろ書き出すには「<< 何とか」をたくさん書かなきゃならないし、書式指定するときはさらに大変! というわけで、できれば使いたくないというのが本音でした。
とはいえ、xprintfは、引数の型をチェックしてくれないとか、およそ昨今の基準では安全でないですし、string型との相性がよくないという問題もあるわけです。それでも、xprintf(というか他言語の書式付き文字列フォーマット)をC++でも使いたい、それでも安全は捨てられない、というのを満たすために、std::formatがC++ 20で導入されました。std::formatは、xprintfのように書式付き文字列に可変数の値を埋め込んで使うことができます。
#include <format> …略… cout << format("Hello, {}!", "World") << endl; // 実行結果:Hello, World!
std::formatを使うには、ヘッダファイル<format>をインクルードします。基本的な使い方として、第1引数に書式付き文字列、第2引数以降に埋め込む値を指定します。書式付き文字列には置換フィールドとして「{}」が使われており、ここに値が埋め込まれます。C#、Rust、Pythonをはじめとするモダンなプログラミング言語で採用されている方式なので、なじみのある方も多いでしょう。
上のリストでは置換フィールドの中身は空でしたが、以下のような書式で記述できます。
{ [引数ID] [: オプション] }
引数IDとは0からの数値であり、参照する引数の順番を指定します。全ての置換フィールドで省略するか、あるいは指定するのかの二択です。オプションは、引数の型によって指定できるものが異なりますが、基本的にxprintf関数と同様に使えます。
auto a = 123; cout << format("{:05d} {:04X} {:08o}", a, a, a) << endl; // 実行結果:00123 007B 00000173 auto s = "August"; cout << format("{:.3s}", s) << endl; // 実行結果:Aug
オプションの書式が誤っていたり、引数が不足する場合にはコンパイル時にエラーとなるので安全です(引数が多い分にはコンパイルエラーとなりません)。
cout << format("こんにちは、{}。") << endl; // コンパイルエラー
なお、C++ 23では、このformat関数を使った出力関数printが実装されました。これは、指定するストリームに書式付き文字列による書き出しを行うものです。冒頭の例をprint関数で書き直すと、以下のようになります。
print(cout, "Hello, {}!\n", "World");
かつてのxprintf関数と同様の感覚で、便利にしかも安全に使えるのはよいですね。