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

我最近在创建 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()在行为上有区别吗?
  • 最后,是否有一个在线参考清楚地记录了这种行为?我的供应商文档(MSVisualStudio2005)似乎没有清楚地显示这一点。
39985 次浏览

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 通过使用在 < a href = “ http://www.cplusplus.com/reference/iostream/宵禁者/setfill/”rel = “ nofollow noReferrer”> setfill中指定的字符填充,只使下一个插入达到指定的宽度

以下评论中的重要说明:

作者: 马丁:

@ Chareles: 那么根据这个要求,所有的机械手都是粘性的。除了 sew,它在使用后似乎会被重置。

作者: 查尔斯:

没错!而 setw 的行为似乎有所不同的唯一原因是,对格式化输出操作有要求。输出流的宽度(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

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[ lib.facet.num.put.virals ] : num_put模板的所有 do_put重载。这些是由取得 basic_ostream和内置数字类型的 operator<<的重载所使用的。

22.2.6.2.2[ lib.locale.money.put.virals ] : money_put模板的所有 do_put重载。

27.6.2.5.4[ lib.ostream. insert ters.role ] : 重载 operator<<,获取一个 basic_ostream和一个 basic _ ostream 实例化或 char的字符类型,带有符号的 charunsigned 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';