哪个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 char
或unsigned char
或者指向这些chartypes数组的指针之一。
说实话,我不确定这个理由,但是没有其他的ostream
状态应该被格式化的输出函数重置。 当然,如果输出操作失败,可能会设置badbit
和failbit
类的东西,但这应该是可以预料的。
我可以考虑重新设置宽度的唯一原因是,如果在尝试输出一些分隔的字段时填充分隔符,可能会出乎意料。
例如
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标志应该说设置,直到另一个操纵器更改。 关于left
, right
和internal
操纵器的一个注意事项:它们像其他的标志一样, 一直坚持到改变。 但是,它们只在设置了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
只使下一个插入指定的宽度