😽

待ちに待ったstd::format。そしてさらに!

に公開

C++ は元々「クラス付きのC言語」として開発されたのですが、C 言語はほぼそのまま C++ に含まれています。でも実際の C に比べると、C++ の中の C は少し使い方や挙動が違っていたりするため、注意しないといけないです。
それは置いといて、C++ には何に関しても、C のやり方と C++ のやり方があります。私は基本 C++ 派ですが、唯一自信を持って C++ の方をお勧めできないのが、出力関数です。

例えば以下のようなシンプルな出力の場合は、C++ の方が使いやすいです。

int ret = doSomething();
printf("doSomething() returned %d\n", ret);  // Cスタイル
std::cout << "doSomething() returned " << ret << std::endl; // C++スタイル
  • printfは改行を「\n」のようにを文字として書かないといけなくて、変数の値を出力するために、「%d」のようにその変数の型に該当する変換指定子を使わないといけないです。しかも、間違えて違う変換指定子を使ってしまうと、警告になりますがコンパイルエラーにならずに未定義動作が発生します。例えば、上記の例でretint型なのにfloat型の「%f」を使ってしまうと、ret-1 の場合、「-1.000000」ではなく「0.000000」が出力されます。
  • std::coutstd::endlのお陰で改行を文字として書かなくてもいいですし、intdouble のような簡単な型は変換指定子なしで出力できます。

こういうシンプルな出力は std::cout の方をお勧めできますが、もっと複雑な出力の場合はどうでしょうか?

const float number1 = 0.5f;
const int number2 = 1;

const std::string text = "text";
int ret = doSomething2(number1, number2, text);
printf("doSomething2(%f, %d, %s) returned %d\n", number1, number2, text.c_str(), ret);

std::cout << "doSomething2(" << number1 << ", " << number2 << ", " << text << ") returned " << ret << std::endl;

こちらはまだシンプルな出力なのに、急にstd::coutの方がかなり読みづらくなりますね。
std::coutは出力を左から右へ書いていくため、出力するメッセージの全体像が非常に見づらいです。途中のコンマや括弧閉じが抜けていても気づかない恐れがあります。
printfの方が全体像が見えますが、変換指定子のせいで使いづらいため、昔は Boost ライブラリのboost::formatという関数を使っていました。
https://boostjp.github.io/tips/format.html

#include <boost/format.hpp>

std::cout << boost::format("doSomething2(%1%, %2%, %3%) returned %4%")
           % number1 % number2 % text.c_str() % ret
          << std::endl;

でも C++20 からようやくboost::formatに似ているstd::formatという機能が追加されました!
https://cpprefjp.github.io/reference/format/format.html

std::formatを使うと、以下になります。

#include <format>

std::cout << std::format("doSomething2({},  {}, {}) returned {}", number1, number2, text, ret)
          << std::endl; 

でもstd::formatstd::stringを返すだけなので、実際の出力は別でしないといけないです。直接出力したい時は少し使いづらいです。

でも安心してください!printfのように直接出力してくれる関数が C++23 で追加されます!
https://cpprefjp.github.io/reference/print/print.html

std::print("doSomething2({},  {}, {}) returned {}\n", number1, number2, text, ret);
std::println("doSomething2({},  {}, {}) returned {}", number1, number2, text, ret);

std::printstd::printlnの違いは、std::printlnは自動的に出力の最後に改行を入れてくれるということだけです。
これでようやく出力関数に関しても C より C++ の方が優れていると胸を張って言えるようになります!


|cpp記事一覧へのリンク|

Discussion