HOWTO · C++

C++ でのブール値の印刷

この記事では、C++ でのブール値のテキスト表現の印刷について説明します。

このページの内容

C++ ではデフォルトで、ブール値は true のとき 1、false のとき 0 として出力されます。機械にとってはそれで十分ですが、ログやデバッグ出力をざっと確認するとき、単独の 1 や 0 は読み違えやすい一方で、true と false はプログラムの意図を読み手に正確に伝えます。

この記事では、C++ でブール値をテキストとして出力する 3 つの方法を紹介します。printf と条件式を使う方法、std::boolalpha ストリームマニピュレーターを使う方法、そして true と false 以外の語を出力するためのカスタム std::numpunct ファセットを使う方法です。

C++ でブール値が 0 と 1 として出力される理由

std::cout は、整数に使っているのと同じ数値処理の仕組みで bool をフォーマットします。ストリームのフォーマットフラグを変更しない限り、true は 1、false は 0 として書き出されます。printf 系も同様に振る舞います。bool は ... 経由で渡されると int に昇格するため、%d でも 1 または 0 が出力されます。

数値出力はコンパクトですが、数字とログ行の海では 1 桁の値は容易に見落とされます。true と false という語を出力すればプログラムの状態がそれ自体で説明されるため、以下の各方法はいずれも読みやすいテキストの出力を対象としています。

printf と条件式でブール値をテキストとして出力する

printf にはブール値専用のフォーマット指定子がなく、引数は int に昇格されるため、%d では 1 か 0 しか生成できません。標準的な回避策は、指定子を %s に切り替え、条件(三項)式 value ? "true" : "false" でテキストを自分で選択することです。

次のプログラムは、同じ値を両方の方法で出力します(コード内のラベルと出力メッセージは英語のまま表示されます)。

#include <cstdio>

int main() {
  bool ready = true;

  std::printf("numeric: %d\n", ready);
  std::printf("text: %s\n", ready ? "true" : "false");
  std::printf("text: %s\n", false ? "true" : "false");

  return 0;
}
numeric: 1
text: true
text: false

この方法は printf が使われる場所ならどこでも機能し、ストリームの状態も必要としません。ただし、すべての呼び出し箇所で条件式を繰り返すと大きなソースファイルではノイズになり、また、選択した文字列が各呼び出し間で一貫することを強制する仕組みもありません。

C++ ストリームで std::boolalpha を使用する

std::boolalpha はストリームマニピュレーターで、ストリームのブール値フォーマットフラグを設定し、bool 値が 1 と 0 ではなく true と false として出力されるようにします。入力ストリームと出力ストリームの両方で機能し、<iostream> がすでにインクルードしている <ios> で宣言されています。

実用上重要な特性は 2 つあります。

  • フラグは持続します。std::boolalpha を一度挿入すると、元に戻すまで、そのストリームに挿入される以降のすべての bool がテキストとして出力されます。
  • std::noboolalpha は、数値表現に戻す対応するマニピュレーターです。

次のプログラムは、両方の状態を示します(コード内のラベルと出力メッセージは英語のまま表示されます)。

#include <iostream>

int main() {
  std::cout << "default: " << true << " / " << false << "\n";

  std::cout << std::boolalpha;
  std::cout << "boolalpha: " << true << " / " << false << "\n";

  std::cout << std::noboolalpha;
  std::cout << "restored: " << true << " / " << false << "\n";

  return 0;
}
default: 1 / 0
boolalpha: true / false
restored: 1 / 0

このフラグはストリーム上で持続するため、以降に出力されるすべての bool に適用されることに注意してください。後続の出力を数値のままにしたい場合は std::noboolalpha を挿入します。また、std::boolalpha が生成するのはロケールが定義する 2 つの値の名前(デフォルトでは true と false)だけです。異なる語が必要な場合は、次のセクションで説明するファセットによる方法が必要です。

std::numpunct ファセットでブール値のテキストをカスタマイズする

ストリームが true と false に使用する語は、ストリームのロケールに含まれる std::numpunct ファセットから取得されます。ストリームの出力操作は std::numput を経由し、そのファセットの do_truename() および do_falsename() メンバーを通じてブール値の名前を問い合わせます。独自のファセットを派生させ、それを含むロケールをストリームに適用すると、std::boolalpha が有効な間、それらの名前を置き換えることができます。

次のプログラムは、独自の語を提供するファセットを組み込みます(コード内のラベルと出力メッセージは英語のまま表示されます)。

#include <iostream>
#include <locale>
#include <string>

class named_bool : public std::numpunct<char> {
 protected:
  std::string do_truename() const override { return "Hai"; }
  std::string do_falsename() const override { return "NAI"; }
};

int main() {
  std::cout.imbue(std::locale(std::locale(), new named_bool));
  std::cout << std::boolalpha;

  std::cout << "custom true: " << true << "\n";
  std::cout << "custom false: " << false << "\n";

  return 0;
}
custom true: Hai
custom false: NAI

Hai と NAI という文字列は、ファセット自身が提供するリテラルです。自動的に翻訳されるものではなく、ストリームは do_truename() と do_falsename() が返すものをそのまま出力します。new named_bool による割り当てがリークすることもありません。ファセットオブジェクトは参照カウントされ、それを参照する最後の std::locale がスコープを抜けたときに破棄されます。

この方法は std::boolalpha が設定されている間だけ有効で、設定しなければストリームは引き続き 1 と 0 を出力します。また、派生クラス、2 つのオーバーライド、imbue 呼び出しが必要となり、3 つの方法の中で最も多くの仕組みを要するため、独自の表現が実際に要件となっている場合に限定して使用してください。

適切なアプローチを選択する

  • C スタイルのコードで 1、2 個の値を扱うだけなら、value ? "true" : "false" を使った printf が最も小さな変更で済みますが、呼び出し箇所ごとに選択ロジックを繰り返します。
  • iostream 出力には std::boolalpha が慣用的な修正方法です。後続の bool 出力を 1/0 に戻す必要がある場合は std::noboolalpha と組み合わせます。
  • True/False のような大文字表記や Hai/NAI のような独自の語ペアなど、語そのものを変更する必要がある場合は、std::numpunct ファセットとそれを適用したロケールが 3 つの中で唯一の選択肢です。std::boolalpha が出力する内容を再定義するためです。

フォーマットの要件を満たす最もシンプルな手段を選んでください。単一の値には条件式、ストリーム出力には std::boolalpha、カスタムのブール名が必要な場合にのみファセットを使用します。