1. ホーム
  2. c++

[解決済み] iomanipのマニピュレーターで「粘着性」があるのはどれ?

2022-05-31 14:44:48

質問

最近 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) では、これを明確に示していないようです。

どのように解決するのですか?

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

マーティンによる

<ブロッククオート

@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 が唯一スティッキーでないマニピュレータのようです。

Charles の場合、チェーン内の次のアイテムにのみ影響を与える簡単なトリックです。

オブジェクトを使用して一時的に状態を変更し、その後オブジェクトを使用して元の状態に戻す方法の例です。

#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