我用 C++ 创建了一个 Vector 类,它非常适合我的问题。我现在正在清理它,并且遇到了以下代码:
std::ostream& operator<<(std::ostream &output, const Vector &v){
output<<"["
<<std::setiosflags(std::ios::right | std::ios::scientific)
<<std::setw(23)
<<std::setprecision(16)
<<v._x<<", "
<<std::setiosflags(std::ios::right | std::ios::scientific)
<<std::setw(23)
<<std::setprecision(16)
<<v._y<<", "
<<std::setiosflags(std::ios::right | std::ios::scientific)
<<std::setw(23)
<<std::setprecision(16)
<<v._z<<"]";
return output;
}
该代码允许将向量打印为 std::cout<。每个数字有 23 个空格,其中 16 个是小数。文本右对齐,以便打印:
1.123456123456e+01
-1.123456123456e+01
而不是
1.123456123456e+01
-1.123456123456e+01
代码看起来非常重复。如何“存储”格式(所有 setiosflags
、setw
和 set precision
语句),以便您可以说“打印字符”以标准方式,但是具有这种给定格式的数字”。
谢谢你!
编辑
根据 Rob Adams 的评论,我将丑陋的代码(正如其他人指出的那样,会扰乱“下一个人”的精度)更改为更简洁(且正确):
std::ostream& operator<<(std::ostream &output, const Vector &v){
std::ios_base::fmtflags f = output.flags(std::ios::right | std::ios::scientific);
std::streamsize p = output.precision(16);
output<<"["
<<std::setw(23)<<v._x<<", "
<<std::setw(23)<<v._y<<", "
<<std::setw(23)<<v._z
<<"]";
output.flags(f);
output.precision(p);
return output;
}
I created a Vector
class in C++ and it works great for my problems. I am now cleaning it up, and I ran into the following piece of code:
std::ostream& operator<<(std::ostream &output, const Vector &v){
output<<"["
<<std::setiosflags(std::ios::right | std::ios::scientific)
<<std::setw(23)
<<std::setprecision(16)
<<v._x<<", "
<<std::setiosflags(std::ios::right | std::ios::scientific)
<<std::setw(23)
<<std::setprecision(16)
<<v._y<<", "
<<std::setiosflags(std::ios::right | std::ios::scientific)
<<std::setw(23)
<<std::setprecision(16)
<<v._z<<"]";
return output;
}
The code allows to print a vector as std::cout<<v<<std::endl;
. Each number has 23 spaces, of which 16 are the decimals. The text is right-aligned so that it will print:
1.123456123456e+01
-1.123456123456e+01
Instead of
1.123456123456e+01
-1.123456123456e+01
The code seems awfully repetitive. How can you "store" the format (all the setiosflags
, setw
and setprecision
statements) such that you can say something like "print the characters in a standard way, but the numbers with this given format".
Thank you!
Edit
As per Rob Adams' comment, I changed my ugly code (which, as pointed out by others, would mess up the precision for the "next guy") to a more succinct (and correct):
std::ostream& operator<<(std::ostream &output, const Vector &v){
std::ios_base::fmtflags f = output.flags(std::ios::right | std::ios::scientific);
std::streamsize p = output.precision(16);
output<<"["
<<std::setw(23)<<v._x<<", "
<<std::setw(23)<<v._y<<", "
<<std::setw(23)<<v._z
<<"]";
output.flags(f);
output.precision(p);
return output;
}
发布评论
评论(4)
只有
std::setw()
是临时的。另外两个调用setiosflags
和set precision
具有永久效果。因此,您可以将代码更改为:
但现在您已经为下一个人取消了标志和精度。试试这个:
最后,如果你绝对必须摆脱常量
23
的重复,你可以这样做(但我不推荐它):另请参阅 另一个问题,他们决定不能使宽度永久化。
Only
std::setw()
is temporary. The other two calls,setiosflags
, andsetprecision
have a permanent effect.So, you could change your code to :
But now you've borked the flags and precision for the next guy. Try this instead:
Finally, if you absolutely have to get rid of the duplication of the constant
23
, you could do something like this (but I wouldn't recommend it):See also this other question, where they decided that you can't make width permanent.
在 C++20 中,您将能够执行以下操作:
与 I/O 操纵器不同,
std::format
不会改变ostream
的格式化状态,从而避免 Bo Persson 提到的问题:它也可以使 I/O 操纵器与
Vector
一起正常工作。在
std::format
可用之前,您可以使用{fmt} 库它是基于。In C++20 you'll be able to do:
Unlike I/O manipulators,
std::format
will not changeostream
's formatting state saving your from the problem mentioned by Bo Persson:It will also work I/O manipulators work correctly with
Vector
.Until
std::format
is available you can use the {fmt} library it is based on.除了 setw() 之外的所有内容实际上都已经这样做了。它们是“粘性的”。
你真正的问题是这个输出之后的下一个输出会发生什么......
Everything but the setw() actually do that already. They are "sticky".
Your real problem is what happens to the next output after this one...
通常,您不会直接使用标准操纵器。在
例如,在这种情况下,您可以定义一个操纵器
fromVector,并使用它:
这样,如果你想改变的宽度和精度
向量中的元素,您只需在一处执行此操作。
在这种情况下,操纵器没有参数,所有这些都是
需要的是一个简单的函数:
当然,这将改变任何格式的大部分格式
后来的用户。一般来说,最好使用
保存状态并恢复状态的临时类对象
在析构函数中。我通常从以下来源获得我的操纵器
类似:
标题:
类 StateSavingManip
{
民众:
状态保存操作(
StateSavingManip const&其他 ) ;
来源:
int getXAlloc() ;
int ourXAlloc = getXAlloc() + 1 ;
如果这样做,则必须在后面添加括号
操纵者,例如:(
我确信一些聪明的灵魂会想出一种方法
避开他们,但他们从来没有打扰过我,所以我没有
打扰了。)
Generally, you don't use the standard manipulators directly. In
this case, for example, you might define a manipulator
fromVector, and use that:
That way, if you want to change the width and precision of
elements in the vector, you only have to do it in one place.
In this case, where the manipulator has no arguments, all that's
needed is a simple function:
Of course, this will have changed most of the formatting for any
later user. In general, it would be better practice to use
a temporary class object which saves the state, and restores it
in the destructor. I usually derive my manipulators from
something like:
header:
class StateSavingManip
{
public:
StateSavingManip(
StateSavingManip const& other ) ;
source:
int getXAlloc() ;
int ourXAlloc = getXAlloc() + 1 ;
If you do this, you'll have to add parentheses after the
manipulator, e.g.:
(I'm sure that some clever soul will figure out a way of
avoiding them, but they've never bothered me, so I haven't
bothered.)