web-dev-qa-db-ja.com

どのiomanipマニピュレーターが「スティッキー」ですか?

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

以下のコメントからの重要なメモ:

マーティン:

@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
82
Martin York

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の他の状態はフォーマットされた出力関数によってリセットされるべきではありません。もちろん、出力操作に障害がある場合は、badbitfailbitのようなものを設定できますが、これは予想されるべきことです。

幅をリセットすることについて考えることができる唯一の理由は、いくつかの区切られたフィールドを出力しようとしたときに、区切り文字がパディングされていた場合は驚くかもしれないということです。

例えば。

_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';
_
30
CB Bailey

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

マニピュレータの完全なリストを見つけることができます こちら 。そのリンクから、すべてのストリームフラグは、別のマニピュレータによって変更されるまで設定されていると言う必要があります。 leftright、および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進数の精度を設定します。

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

6
David Brown