どのiomanipマニピュレーターが「粘着性」がありますか?


140

私は最近、明示的に変更するまで、すべての挿入の文字列ストリームに影響を与える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)はこれを明確に示していないようです。

作業ラウンドはここにあります:stackoverflow.com/a/37495361/984471
Manohar Reddy Poreddy

回答:


87

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

マーティン:

@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

いいチートシート。情報がどこから来たかへの参照を追加してください、そしてそれは完全な答えでしょう。
マークランサム

1
ただし、オブジェクトを返しますが、setfill()が実際に「スティッキー」であることを確認できます。だから私はこの答えは正しくないと思います。
ジョンK

2
ストリームを返すオブジェクトはスティッキーである必要がありますが、オブジェクトを返すオブジェクトはスティッキである可能性がありますが、必須ではありません。ジョンの情報で回答を更新します。
マーティンヨーク

1
私はあなたの推論を理解しているかどうかわかりません。パラメータを使用するすべてのマニピュレータは、そのオブジェクトがストリームに挿入されたときにストリームに作用する未指定のオブジェクトを返すフリー関数として実装されます。これは、パラメータ付きの挿入構文を保持する唯一の(?)方法であるためです。いずれにせよoperator<<、マニピュレーターに適切なは、ストリームの状態が特定の方法で変更されることを保証します。どちらのフォームも、いかなる種類の国家歩哨もセットアップしません。状態のどの部分がリセットされるかを決定するのは、次にフォーマットされた挿入操作の動作のみです。
CBベイリー

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

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。これらはoperator<<、a 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<<a を取得するオーバーロードbasic_ostreamと、basic_ostreamインスタンス化のcharタイプの1つまたはchar、signed 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';

6

setw()次の挿入にのみ影響します。それがまさにそのsetw()ふるまいです。の動作はsetw()と同じios_base::width()です。私は自分の持ってsetw()からの情報をcplusplus.com

マニピュレーターの完全なリストは、こちらで確認できます。そのリンクから、別のマニピュレータによって変更されるまで、すべてのストリームフラグが設定されていると言う必要があります。一つに関する注意leftrightおよび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手動でフラグを設定できます。そのリストはここにあるので、粘着性があります。

resetiosflagssetiosflags指定されたフラグの設定を解除することを除いて、と同様に動作します。

setbase ストリームに挿入される整数のベースを設定します(したがって、ベース16の17は「11」になり、ベース2の「10001」になります)。

setfillsetw使用時にストリームに挿入するフィル文字を設定します。

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

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


まあ、それらのほとんどはフラグを設定しているだけなので、それらは「粘着性」があります。setw()は、1つの挿入だけに影響を与える唯一のもののようです。それぞれの詳細については、cplusplus.com / reference / iostream / manipulators
David Brown

まあstd::hexも粘着性ではないと、明らかに、std::flushまたはstd::setiosflagsいずれかの粘着性はありません。そんなに簡単だとは思いません。
sbi 2009年

hexとsetiosflags()をテストするだけで、どちらもスティッキーであるように見えます(どちらも、変更するまでそのストリームに対して持続するフラグを設定するだけです)。
David Brown、

うん、ベタつかstd::hexないと主張したウェブページは間違っていた-私もこれを見つけた。ただし、ストリームフラグは、std::setiosflags再度挿入しなくても変更される可能性があるため、これを非固定と見なす可能性があります。また、std::wsべたつきもありません。それはそうはないことは容易。
sbi 2009年

あなたはあなたの答えを改善するためにかなりの努力をしました。+1
sbi 2009年
弊社のサイトを使用することにより、あなたは弊社のクッキーポリシーおよびプライバシーポリシーを読み、理解したものとみなされます。
Licensed under cc by-sa 3.0 with attribution required.