哪些 iomanip 操纵器是“粘性的”?

新手上路,请多包涵

我最近在创建 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)似乎没有清楚地表明这一点。

原文由 John K 发布,翻译遵循 CC BY-SA 4.0 许可协议

阅读 842
2 个回答

以下评论的重要说明:

马丁:

@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

原文由 Martin York 发布,翻译遵循 CC BY-SA 3.0 许可协议

setw() 只影响下一次插入。这就是 setw() 的行为方式。 --- 的行为与 setw() ios_base::width() 。我从 cplusplus.com 获得了我的 — setw() 信息。

您可以 在此处 找到完整的操纵器列表。从该链接中,所有流标志都应保持设置,直到被另一个操纵器更改。关于 leftrightinternal 操纵器的注意事项:它们与其他标志一样,并且 一直存在直到更改。但是,它们只有在设置了流的宽度时才起作用,并且必须每行设置宽度。所以,例如

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

输入和输出操纵器不具有粘性,仅在使用它们的地方出现一次。参数化的操纵器各不相同,以下是每个的简要说明:

setiosflags 让您手动设置标志,可以在 此处 找到其列表,因此具有粘性。

resetiosflags 行为类似于 setiosflags 除了它取消设置指定的标志。

setbase 设置插入到流中的整数的基数(所以基数 16 中的 17 将是“11”,而基数 2 中的将是“10001”)。

setfill 设置填充字符以在使用 setw 时插入到流中。

setprecision 设置插入浮点值时使用的十进制精度。

setw 通过填充 setfill

原文由 David Brown 发布,翻译遵循 CC BY-SA 4.0 许可协议

撰写回答
你尚未登录,登录后可以
  • 和开发者交流问题的细节
  • 关注并接收问题和回答的更新提醒
  • 参与内容的编辑和改进,让解决方法与时俱进
推荐问题