152

最近、明示的に変更するまで、挿入ごとに文字列ストリームに影響を与えるstringstreamと誤って想定していたため、作成に問題がありました。std::setw()ただし、挿入後は常に設定が解除されます。

// 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()ますか?
  • 最後に、この動作を明確に文書化したオンラインリファレンスはありますか?私のベンダーのドキュメント(MS Visual Studio 2005)は、これを明確に示していないようです。
4

3 に答える 3

100

以下のコメントからの重要な注意事項:

マーティン著:

@Chareles: 次に、この要件により、すべてのマニピュレーターがスティッキーになります。使用後にリセットされるように見える setw を除きます。

チャールズ著:

丁度!setw の動作が異なるように見える唯一の理由は、出力ストリームを明示的に .width(0) にするフォーマットされた出力操作に関する要件があるためです。

以下は、上記の結論につながる議論です。


コードを見ると、次のマニピュレータはストリームではなくオブジェクトを返します。

setiosflags
resetiosflags
setbase
setfill
setprecision
setw

これは、ストリームに適用される次のオブジェクトのみに操作を適用する一般的な手法です。残念ながら、これは粘着性を排除するものではありません。テストは、それらを除くすべてsetwが粘着性であることを示しています。

setiosflags:  Sticky
resetiosflags:Sticky
setbase:      Sticky
setfill:      Sticky
setprecision: 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
于 2009-10-07T17:56:55.397 に答える
31

width「スティッキー」に見えない理由は、特定の操作が.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これらは、組み込みの数値型と aを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]: basic_ostream インスタンス化の char 型のoperator<<aと 1 つ、またはこれらの char 型の配列への符号付きorまたはポインターのオーバーロード。basic_ostreamcharcharunsigned 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';
于 2009-10-07T19:42:34.650 に答える
6

setw()次の挿入のみに影響します。それはまさにsetw()振る舞い方です。の動作はsetw()と同じios_base::width()です。cplusplus.comsetw()から情報を入手しました。

マニピュレータの完全なリストは、ここにあります。そのリンクから、別のマニピュレータによって変更されるまで、すべてのストリーム フラグが設定されているはずです。leftrightおよびマニピュレータに関する 1 つの注意事項internal: これらは他のフラグと同様であり、変更されるまで保持されますただし、ストリームの幅が設定されている場合にのみ効果があり、幅は行ごとに設定する必要があります。たとえば、

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フラグを手動で設定できます。そのリストはここにあるので、粘着性があります。

resetiosflagsと同様に動作しsetiosflagsますが、指定されたフラグを設定解除します。

setbaseストリームに挿入される整数の基数を設定します (基数 16 の 17 は "11"、基数 2 では "10001" になります)。

setfillが使用されたときにストリームに挿入する埋め込み文字を設定しますsetw

setprecision浮動小数点値を挿入するときに使用する 10 進精度を設定します。

setwで指定された文字で埋めることにより、次の挿入のみが指定された幅になりますsetfill

于 2009-10-07T16:23:21.073 に答える