ストリームマニピュレーターはどのように機能しますか?
質問
ユーザーがこのようなストリームマニピュレーターを定義できることはよく知られています。
ostream& tab(ostream & output)
{
return output<< '\t';
}
そして、これはで使用できます 主要() このような:
cout<<'a'<<tab<<'b'<<'c'<<endl;
私に説明してくださいこれはどのように機能しますか?もしも オペレーター< 2番目のパラメーターとして想定します。 オストリーム&, 、なぜそれが必要なのかを説明してください。関数が取られて返されない場合、何が間違っているでしょうか オストリーム& しかし、そうでした 空所 それ以外の オストリーム&?
また、「DEC」、「HEX」マニピュレーターがそれらの間で変更されないまで有効になる理由は興味深いですが、各ストリーミングに有効にするためには、ユーザー定義のマニピュレーターを常に使用する必要がありますか?
解決
標準は以下を定義します operator<<
の過負荷 basic_ostream
クラステンプレート:
basic_ostream<charT,traits>& operator<<(
basic_ostream<charT,traits>& (*pf) (basic_ostream<charT,traits>&) );
効果:なし。フォーマットされた出力関数として動作しません(27.6.2.5.1で説明されています)。
戻り値:
pf(*this)
.
パラメーターは、参照を取得して返す関数へのポインターです std::ostream
.
これは、この署名で関数を「ストリーミング」できることを意味します。 ostream
オブジェクトと、その関数をストリームに呼び出す効果があります。式で関数の名前を使用する場合、それは(通常)その関数へのポインターに変換されます。
std::hex
です std::ios_base
次のように定義されたマニピュレーター。
ios_base& hex(ios_base& str);
効果:呼び出し
str.setf(ios_base::hex, ios_base::basefield)
.返品:str。
これは、そのストリーミングを意味します hex
に ostream
出力ベースフォーマットフラグを16進数に出力数に設定します。マニピュレーターはそれ自体を出力しません。
他のヒント
それには何の問題もありません<<オペレーターが定義されている<<オペレーターはありません。 <<の既存の過負荷は、署名のあるマニピュレーターを期待しています ostream&(*fp)(ostream&).
タイプのマニピュレーターを与えた場合 OstReam&(*fp)( コンパイラエラーが発生します ではない の定義があります operator <<(ostream&、、ostream&(*fp)())). 。この機能が必要な場合は、このタイプのマニピュレーターを受け入れるために<<オペレーターに過負荷する必要があります。
これの定義を書く必要があります。
Ostream&Ostream :: operator <<(ostream&(*m)())
ここでは魔法のことは何も起こっていないことに留意してください。ストリームライブラリは大きく依存しています 標準 C ++機能:オペレーターの過負荷、クラス、参照。
説明した機能をどのように作成できるかがわかったので、ここに私たちがしない理由は次のとおりです。
操作しようとしているストリームへの参照を渡さずに、最終デバイスに接続されたストリーム(CIN、OUT、ERR、FSTREAMなど)を変更することはできません。関数(モディファイアはすべてファンシーな名前の関数にすぎません)は、<<オペレーターの左側のものとは何の関係もない新しいオストリーを返す必要があるか、非常にugいメカニズムを介して、どのオストリーがすべきかを把握する必要があります。モディファイアの右側にあるすべてのものと接続すると、最終的なデバイスに到達しますが、返された関数/修飾子がどのオストリーにも送信されます。
このようなストリームを考えてください
cout << "something here" << tab << "something else"<< endl;
本当に意味があります
(((cout << "something here") << tab ) << "something else" ) << endl);
括弧の各セットが何かをして(書き込み、変更など)、次の括弧のセットが動作できるようにカウトを返します。
TABモディファイア/関数がOstReamへの参照を取得しなかった場合、タスクを実行するために<<オペレーターの左に左右者が何であるかを推測する必要があります。 Cour、Cerr、File Streamを使用していましたか...?関数の内部は、その情報をどのように手に入れない限り、そしてそれを参照するのと同じくらい単純ではないのかを知ることは決してありません。
さて、本当にポイントを家に持ち帰るために、何を見てみましょう endl 実際に、そして私たちが使用している<<オペレーターの過負荷バージョン:
このオペレーターは次のようになります:
ostream& ostream::operator<<(ostream& (*m)(ostream&))
{
return (*m)(*this);
}
endlは次のようになります:
ostream& endl(ostream& os)
{
os << '\n';
os.flush();
return os;
}
ENDLの目的は、新しいラインを追加してストリームをフラッシュし、ストリームの内部バッファのすべての内容がデバイスに書き込まれていることを確認することです。これを行うには、まずこのストリームに「 n」を書き込む必要があります。その後、ストリームにフラッシュするように指示する必要があります。 ENDLがどのストリームに書き込みとフラッシュするかを知る唯一の方法は、オペレーターがその情報を呼び出すときにその情報をENDL関数に渡すことです。それは私があなたに私の車を洗うように言っているようになりますが、完全な駐車場でどの車が私のものであるかをあなたに伝えることは決してありません。あなたはあなたの仕事を成し遂げることができないでしょう。あなたは私があなたに私の車を渡すか、自分でそれを洗うことができるかどうかを私に必要です。
それが物事を解決することを願っています
PS-偶然に私の車を見つけた場合は、洗ってください。
通常、ストリームマニピュレーターは、ストリームオブジェクトにいくつかのフラグ(またはその他の設定)を設定するため、次回使用されるときにフラグに従って機能します。したがって、マニピュレーターは、渡されたのと同じオブジェクトを返します。 operator<<
もちろん、マニピュレーターと呼ばれるマニピュレーターと呼ばれる過負荷はすでにこのオブジェクトを持っているので、あなたが気づいたように、そのケースでは厳密に必要はありません。これはすべての標準マニピュレーターをカバーしていると思います - それらはすべて入力を返します。
ただし、返品値を使用すると、フレームワークはカスタムストリームマニピュレーターほど柔軟です たぶん......だろう 別のオブジェクトを返します。おそらく、与えられたオブジェクトのラッパーです。その後、この他のオブジェクトはから返されます cout << 'a' << tab
, 、そして、組み込みの何かをすることができます ostream
設定のフォーマットはサポートされていません。
ただし、この他のオブジェクトがどのように解放されるかわからないので、これがどれほど実用的かわかりません。それは、によって管理されているプロキシオブジェクトのように、特異なものでなければならないかもしれません ostream
自体。その後、マニピュレーターは、それを積極的にサポートするカスタムストリームクラスでのみ機能します。これは通常、マニピュレーターのポイントではありません。