哪个iomanip操纵器是'粘'?

我最近有一个问题,创build一个stringstream,因为我错误地认为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()之间的行为有差异吗?
  • 最后是否有一个在线参考清楚地logging了这种行为? 我的供应商文档(MS Visual Studio 2005)似乎没有清楚地表明这一点。

以下评论的重要说明:

马丁:

@Chareles:那么通过这个要求,所有的操作都是粘性的。 除了似乎在使用后重置的setw。

查尔斯:

究竟! setw似乎performance出不同的唯一原因是因为要求格式化的输出操作显式地.width(0)输出stream。

以下是导致上述结论的讨论:


看代码,下面的操作符返回一个对象而不是stream:

 setiosflags resetiosflags setbase setfill setprecision setw 

这是将操作仅应用于应用于stream的下一个对象的常用技术。 不幸的是,这并不妨碍他们的粘性。 testing表明,除了setw之外,他们都是粘性的。

 setiosflags: Sticky resetiosflags:Sticky setbase: Sticky setfill: Sticky setprecision: Sticky 

所有其他操纵符都返回一个stream对象。 因此,他们改变的任何状态信息都必须logging在stream对象中,因此是永久的(直到另一个操纵者改变状态)。 因此,以下操纵器必须是粘性操纵器。

 [no]boolalpha [no]showbase [no]showpoint [no]showpos [no]skipws [no]unitbuf [no]uppercase dec/ hex/ oct fixed/ scientific internal/ left/ right 

这些操纵符实际上是在stream本身上执行一个操作,而不是stream对象(尽pipe在技术上stream是stream对象状态的一部分)。 但我不相信它们会影响stream对象状态的任何其他部分。

 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看起来不“粘”的原因是某些操作保证在输出stream上调用.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]: num_put模板的所有do_put重载。 这些被operator<<重载使用basic_ostream和一个内置的数字types。

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

27.6.2.5.4 [lib.ostream.inserters.character]:重载operator<<basic_ostream和chartypes之一的char ,signed charunsigned char或者指向这些chartypes数组的指针之一。

说实话,我不确定这个理由,但是没有其他的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'; 

setw()仅影响下一个插入。 这只是setw()行为方式。 setw()的行为与ios_base::width() 。 我从cplusplus.com得到了我的setw()信息。

你可以在这里find完整的操纵器列表。 从这个链接,所有的stream标志应该说设置,直到另一个操纵器更改。 关于leftrightinternal操纵器的一个注意事项:它们像其他的标志一样, 一直坚持到改变。 但是,它们只在设置了stream的宽度时才起作用,并且必须在每一行中设置宽度。 所以,例如

 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 

input和输出操纵器不是粘性的,只在使用它们的地方出现。 参数化的操纵器各不相同,下面分别对其进行简要描述:

setiosflags让你手动设置标志,其中的列表可以从这里来 ,所以它是粘性的。

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

setbase设置插入到stream中的整数的基数(因此基数16中的17将是“11”,基数2中是“10001”)。

使用setw时, setfill设置填充字符以插入stream中。

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

通过填充setfill指定的字符, setw只使下一个插入指定的宽度