可以将文章内容翻译成中文,广告屏蔽插件可能会导致该功能失效(如失效,请关闭广告屏蔽插件后再试):
问题:
I recently had a problem creating a stringstream
due to the fact that I incorrectly assumed std::setw()
would affect the stringstream for every insertion, until I changed it explicitly. However, it is always unset after the insertion.
// 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\'
So, I have a number of questions:
- Why is
setw()
this way?
- Are any other manipulators this way?
- Is there a difference in behavior between
std::ios_base::width()
and std::setw()
?
- Finally is there an online reference that clearly documents this behavior? My vendor documentation (MS Visual Studio 2005) doesn\'t seem to clearly show this.
回答1:
Important notes from the comments below:
By Martin:
@Chareles: Then by this requirement all manipulators are sticky. Except setw which seems to be reset after use.
By Charles:
Exactly! and the only reason that setw appears to behave differently is because there are requirements on formatted output operations to explicitly .width(0) the output stream.
The following is the discussion that lead to the above conclusion:
Looking at the code the following manipulators return an object rather than a stream:
setiosflags
resetiosflags
setbase
setfill
setprecision
setw
This is a common technique to apply an operation to only the next object that is applied to the stream. Unfortunately this does not preclude them from being sticky. Tests indicate that all of them except setw
are sticky.
setiosflags: Sticky
resetiosflags:Sticky
setbase: Sticky
setfill: Sticky
setprecision: Sticky
All the other manipulators return a stream object. Thus any state information they change must be recorded in the stream object and is thus permanent (until another manipulator changes the state). Thus the following manipulators must be Sticky manipulators.
[no]boolalpha
[no]showbase
[no]showpoint
[no]showpos
[no]skipws
[no]unitbuf
[no]uppercase
dec/ hex/ oct
fixed/ scientific
internal/ left/ right
These manipulators actually perform an operation on the stream itself rather than the stream object (Though technically the stream is part of the stream objects state). But I do not believe they affect any other part of the stream objects state.
ws/ endl/ ends/ flush
The conclusion is that setw seems to be the only manipulator on my version that is not sticky.
For Charles a simple trick to affect only the next item in the chain:
Here is an Example how an object can be used to temporaily change the state then put it back by the use of an object:
#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
回答2:
The reason that width
does not appear to be \'sticky\' is that certain operations are guaranteed to call .width(0)
on an output stream. Those are:
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]: All do_put
overloads for the num_put
template. These are used by overloads of operator<<
taking a basic_ostream
and a built in numeric type.
22.2.6.2.2 [lib.locale.money.put.virtuals]: All do_put
overloads for the money_put
template.
27.6.2.5.4 [lib.ostream.inserters.character]: Overloads of operator<<
taking a basic_ostream
and one of the char type of the basic_ostream instantiation or char
, signed char
or unsigned char
or pointers to arrays of these char types.
To be honest I\'m not sure of the rationale for this, but no other states of an ostream
should be reset by formatted output functions. Of course, things like badbit
and failbit
may be set if there is a failure in the output operation, but that should be expected.
The only reason that I can think of for resetting the width is that it might be surprising if, when trying to output some delimited fields, your delimiters were padded.
E.g.
std::cout << std::setw(6) << 4.5 << \'|\' << 3.6 << \'\\n\';
\" 4.5 | 3.6 \\n\"
To \'correct\' this would take:
std::cout << std::setw(6) << 4.5 << std::setw(0) << \'|\' << std::setw(6) << 3.6 << std::setw(0) << \'\\n\';
whereas with a resetting width, the desired output can be generated with the shorter:
std::cout << std::setw(6) << 4.5 << \'|\' << std::setw(6) << 3.6 << \'\\n\';
回答3:
setw()
only affects the next insertion. That\'s just the way setw()
behaves. The behavior of setw()
is the same as ios_base::width()
. I got my setw()
information from cplusplus.com.
You can find a full list of manipulators here. From that link, all the stream flags should say set until changed by another manipulator. One note about the left
, right
and internal
manipulators: They are like the other flags and do persist until changed. However, they only have an effect when the width of the stream is set, and the width must be set every line. So, for example
cout.width(6);
cout << right << \"a\" << endl;
cout.width(6);
cout << \"b\" << endl;
cout.width(6);
cout << \"c\" << endl;
would give you
> a
> b
> c
but
cout.width(6);
cout << right << \"a\" << endl;
cout << \"b\" << endl;
cout << \"c\" << endl;
would give you
> a
>b
>c
The Input and Output manipulators are not sticky and only occur once where they are used. The parametrized manipulators are each different, here\'s a brief description of each:
setiosflags
lets you manually set flags, a list of which can be fount here, so it is sticky.
resetiosflags
behaves the similar to setiosflags
except it unsets the specified flags.
setbase
sets the base of integers inserted into the stream (so 17 in base 16 would be \"11\", and in base 2 would be \"10001\").
setfill
sets the fill character to insert in the stream when setw
is used.
setprecision
sets the decimal precision to be used when inserting floating point values.
setw
makes only the next insertion the specified width by filling with the character specified in setfill