明示的に変更するまで、std::setw()
がすべての挿入の文字列ストリームに影響すると誤って想定していたため、最近stringstream
の作成で問題が発生しました。ただし、挿入後は常に設定解除されます。
_// With timestruct with value of 'Oct 7 9:04 AM'
std::stringstream ss;
ss.fill('0'); ss.setf(ios::right, ios::adjustfield);
ss << setw(2) << timestruct.tm_mday;
ss << timestruct.tm_hour;
ss << timestruct.tm_min;
std::string filingTime = ss.str(); // BAD: '0794'
_
だから、私はいくつかの質問があります:
setw()
がこのようになっているのですか?std::ios_base::width()
とstd::setw()
の動作に違いはありますか?以下のコメントからの重要なメモ:
マーティン:
@Chareles:この要件により、すべてのマニピュレーターはスティッキーです。使用後にリセットされると思われるsetwを除きます。
チャールズ:
まさに! setwが異なる動作をするように見える唯一の理由は、出力ストリームを明示的に.width(0)するためのフォーマットされた出力操作に対する要件があるためです。
以下は、上記の結論に至る議論です。
コードを見ると、次のマニピュレーターはストリームではなくオブジェクトを返します。
setiosflags
resetiosflags
setbase
setfill
setprecision
setw
これは、ストリームに適用される次のオブジェクトにのみ操作を適用する一般的な手法です。残念ながら、これはそれらが粘着性であることを妨げるものではありません。テストでは、setw
以外のすべてがスティッキーであることが示されています。
setiosflags: Sticky
resetiosflags:Sticky
setbase: Sticky
setfill: Sticky
setprecision: Sticky
他のすべてのマニピュレータは、ストリームオブジェクトを返します。したがって、変更する状態情報はストリームオブジェクトに記録する必要があり、永続的です(別のマニピュレーターが状態を変更するまで)。したがって、次のマニピュレーターはStickyマニピュレーターでなければなりません。
[no]boolalpha
[no]showbase
[no]showpoint
[no]showpos
[no]skipws
[no]unitbuf
[no]uppercase
dec/ hex/ oct
fixed/ scientific
internal/ left/ right
これらのマニピュレータは、ストリームオブジェクトではなく、ストリーム自体に対して実際に操作を実行します(技術的には、ストリームはストリームオブジェクトの状態の一部です)。しかし、ストリームオブジェクトの状態の他の部分に影響を与えるとは思わない。
ws/ endl/ ends/ flush
結論は、setwが、私のバージョンでは粘着性のない唯一のマニピュレーターであるようだということです。
チャールズにとって、チェーン内の次のアイテムのみに影響を与える簡単なトリック:
オブジェクトを使用して、一時的に状態を変更し、オブジェクトを使用して元に戻す方法の例を次に示します。
#include <iostream>
#include <iomanip>
// Private object constructed by the format object PutSquareBracket
struct SquareBracktAroundNextItem
{
SquareBracktAroundNextItem(std::ostream& str)
:m_str(str)
{}
std::ostream& m_str;
};
// New Format Object
struct PutSquareBracket
{};
// Format object passed to stream.
// All it does is return an object that can maintain state away from the
// stream object (so that it is not STICKY)
SquareBracktAroundNextItem operator<<(std::ostream& str,PutSquareBracket const& data)
{
return SquareBracktAroundNextItem(str);
}
// The Non Sticky formatting.
// Here we temporariy set formating to fixed with a precision of 10.
// After the next value is printed we return the stream to the original state
// Then return the stream for normal processing.
template<typename T>
std::ostream& operator<<(SquareBracktAroundNextItem const& bracket,T const& data)
{
std::ios_base::fmtflags flags = bracket.m_str.flags();
std::streamsize currentPrecision = bracket.m_str.precision();
bracket.m_str << '[' << std::fixed << std::setprecision(10) << data << std::setprecision(currentPrecision) << ']';
bracket.m_str.flags(flags);
return bracket.m_str;
}
int main()
{
std::cout << 5.34 << "\n" // Before
<< PutSquareBracket() << 5.34 << "\n" // Temp change settings.
<< 5.34 << "\n"; // After
}
> ./a.out
5.34
[5.3400000000]
5.34
width
が 'sticky'に見えないのは、特定の操作が出力ストリームで.width(0)
を呼び出すことが保証されているためです。それらは:
21.3.7.9 [lib.string.io]:
_template<class charT, class traits, class Allocator>
basic_ostream<charT, traits>&
operator<<(basic_ostream<charT, traits>& os,
const basic_string<charT,traits,Allocator>& str);
_
22.2.2.2.2 [lib.facet.num.put.virtuals]:_do_put
_テンプレートのすべての_num_put
_オーバーロード。これらは_operator<<
_のオーバーロードで_basic_ostream
_と組み込み数値型を使用して使用されます。
22.2.6.2.2 [lib.locale.money.put.virtuals]:_do_put
_テンプレートのすべての_money_put
_オーバーロード。
27.6.2.5.4 [lib.ostream.inserters.character]:_operator<<
_のオーバーロードと_basic_ostream
_およびbasic_ostreamインスタンス化のchar型の1つ、またはchar
、符号付きchar
または_unsigned char
_またはこれらのchar型の配列へのポインター。
正直に言うと、この理由はわかりませんが、ostream
の他の状態はフォーマットされた出力関数によってリセットされるべきではありません。もちろん、出力操作に障害がある場合は、badbit
やfailbit
のようなものを設定できますが、これは予想されるべきことです。
幅をリセットすることについて考えることができる唯一の理由は、いくつかの区切られたフィールドを出力しようとしたときに、区切り文字がパディングされていた場合は驚くかもしれないということです。
例えば。
_std::cout << std::setw(6) << 4.5 << '|' << 3.6 << '\n';
" 4.5 | 3.6 \n"
_
これを「修正」するには:
_std::cout << std::setw(6) << 4.5 << std::setw(0) << '|' << std::setw(6) << 3.6 << std::setw(0) << '\n';
_
一方、幅をリセットすると、短い出力で目的の出力を生成できます。
_std::cout << std::setw(6) << 4.5 << '|' << std::setw(6) << 3.6 << '\n';
_
setw()
は次の挿入にのみ影響します。それはsetw()
の振る舞いです。 setw()
の動作はios_base::width()
と同じです。 cplusplus.com からsetw()
情報を取得しました。
マニピュレータの完全なリストを見つけることができます こちら 。そのリンクから、すべてのストリームフラグは、別のマニピュレータによって変更されるまで設定されていると言う必要があります。 left
、right
、およびinternal
マニピュレーターに関する1つの注意:これらは他のフラグと同様であり、doは変更されるまで持続します。ただし、ストリームの幅が設定されている場合にのみ効果があり、幅は行ごとに設定する必要があります。だから、例えば
cout.width(6);
cout << right << "a" << endl;
cout.width(6);
cout << "b" << endl;
cout.width(6);
cout << "c" << endl;
あなたに与えるだろう
> a
> b
> c
しかし
cout.width(6);
cout << right << "a" << endl;
cout << "b" << endl;
cout << "c" << endl;
あなたに与えるだろう
> a
>b
>c
入力および出力マニピュレーターはスティッキーではなく、使用される場所で1回だけ発生します。パラメーター化されたマニピュレーターはそれぞれ異なります。それぞれの簡単な説明を次に示します。
setiosflags
フラグを手動で設定できます。フラグのリストは here であるため、スティッキーです。
resetiosflags
は、指定されたフラグを設定解除することを除いて、 setiosflags
と同様に動作します。
setbase
は、ストリームに挿入される整数の基数を設定します(したがって、基数16の17は「11」、基数2は「10001」になります)。
setfill
は、setw
が使用されるときにストリームに挿入する塗り文字を設定します。
setprecision
は、浮動小数点値を挿入するときに使用される10進数の精度を設定します。