1. ホーム
  2. c++

[解決済み] std::flushはどのように機能するのですか?

2023-01-17 22:06:51

質問

どなたか、次のようなことを (できれば平易な英語で) 説明していただけないでしょうか。 std::flush がどのように機能するのかを説明していただけませんか?

  • それは何ですか?
  • ストリームを流すのはいつですか?
  • なぜそれが重要なのですか?

ありがとうございました。

どのように解決するのですか?

回答がないため std::flush がたまたまそうであっただけで、実際にはどうなのかについて、ここで詳しく説明します。 std::flush マニピュレーター つまり、特定のシグネチャを持つ関数です。まず最初に、簡単な例として std::flush というシグネチャを持つ

std::ostream& std::flush(std::ostream&);

しかし、現実はもう少し複雑です(興味のある方は、以下でも解説しています)。

ストリームクラスのオーバーロード出力演算子は、このような形式の演算子を取ります。つまり、マニピュレータを引数に取るメンバ関数が存在します。出力演算子は、オブジェクトそのものを使ってマニピュレータを呼び出す。

std::ostream& std::ostream::operator<< (std::ostream& (*manip)(std::ostream&)) {
    (*manip)(*this);
    return *this;
}

つまり、"output"すると。 std::flushstd::ostream を指定した場合、対応する関数を呼び出すだけであり、つまり、以下の2つの記述は等価である。

std::cout << std::flush;
std::flush(std::cout);

今すぐ std::flush() 自体はかなり単純です。このメソッドが行うのは std::ostream::flush() を呼び出すだけです。つまり、次のような実装を想定してください。

std::ostream& std::flush(std::ostream& out) {
    out.flush();
    return out;
}

std::ostream::flush() 関数は、技術的には std::streambuf::pubsync() を呼び出します。ストリームバッファは、文字をバッファリングし、使用中のバッファがオーバーフローしたときや、内部表現が外部の宛先と同期されるべきとき、つまりデータがフラッシュされるときに、文字を外部の宛先に送信する役割を担います。シーケンシャルストリームでは、外部送信先との同期とは、バッファリングされた文字が直ちに送信されることを意味するだけである。つまり std::flush を使うと、ストリームバッファはその出力バッファをフラッシュします。たとえば、コンソールにデータが書き込まれたとき、フラッシュするとコンソールのこの位置に文字が表示されます。

ここで疑問が湧くかもしれません。なぜ、文字はすぐに書き込まれないのでしょうか? 簡単な答えは、文字の書き込みは一般的にかなり遅いということです。しかし、妥当な量の文字を書き込むのにかかる時間は、1 つの場所だけを書き込むのと本質的に同じです。OSやファイルシステムなどの特性にもよりますが、4k文字程度までなら1文字とほぼ同じ時間で書けることが多いです。したがって、外部の宛先の詳細に応じてバッファを使用して文字を送信する前にバッファリングすることは、大きなパフォーマンスの改善になります。

以上、3つの質問のうち2つにお答えしました。残りの質問は、「いつストリームをフラッシュするのか」です。答えはこうです。文字が外部の宛先に書き込まれるべき時です! これは、ファイル書き込みの終了時 (ファイルを閉じると暗黙のうちにバッファがフラッシュされます) か、ユーザ入力を要求する直前 (ただし std::cout から読み込むときに自動的にフラッシュされます)。 std::cin として std::coutstd::istream::tie() になります。 std::cin ). 明示的にストリームをフラッシュしたい場合もあるかもしれませんが、それはかなりまれなことだと思います。

最後に、私が約束したのは std::flush が実際にどのようなものであるかを説明することを約束しました。ストリームは、異なる文字型を扱うことができるクラステンプレートです (実際に動作するのは charwchar_t といった具合に、他の文字と組み合わせて使うことは、かなり難しいのですが、本当に決心すれば可能です。) を使用できるようにするには std::flush をストリームのすべてのインスタンスで使用できるようにするには、次のようなシグネチャを持つ関数テンプレートにする必要があります。

template <typename cT, typename Traits>
std::basic_ostream<cT, Traits>& std::flush(std::basic_ostream<cT, Traits>&);

を使う場合 std::flush のインスタンス化ですぐに std::basic_ostream のインスタンス化であっても、それは本当に重要ではありません。コンパイラは自動的にテンプレート引数を推論します。しかし、この関数がテンプレート引数の推論を容易にする何かと一緒に言及されていない場合、コンパイラはテンプレート引数の推論に失敗します。