我在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<<v<<std::endl;
。每个数字有23个空格,其中16个是小数。文本右对齐,以便打印:
1.123456123456e+01
-1.123456123456e+01
而不是
1.123456123456e+01
-1.123456123456e+01
代码似乎非常重复。如何“存储”格式(所有setiosflags
,setw
和setprecision
语句),以便您可以说“以标准方式打印字符,但数字与这个给定的格式“。
谢谢!
修改的
根据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;
}
答案 0 :(得分:10)
只有std::setw()
是暂时的。其他两个电话setiosflags
和setprecision
会产生永久性影响。
因此,您可以将代码更改为:
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::setw(23)
<<v._y<<", "
<<std::setw(23)
<<v._z<<"]";
return output;
}
但是现在你已经为下一个人加了旗子和精确度。试试这个:
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;
}
最后,如果你必须摆脱常量23
的重复,你可以做这样的事情(但我不推荐它):
struct width {
int w;
width(int w) : w(w) {}
friend std::ostream& operator<<(std::ostream&os, const width& w) {
return os << std::setw(width.w);
}
};
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);
width w(23);
output<<"["
<<w
<<v._x<<", "
<<w
<<v._y<<", "
<<w
<<v._z<<"]";
output.flags(f);
output.precision(p);
return output;
}
另见this other question,他们决定不能使宽度永久。
答案 1 :(得分:3)
在 C++20 中,您将能够:
std::ostream& operator<<(std::ostream& output, const Vector& v){
const int width = 23, precision = 16;
return output << std::format(
"[{0:{3}.{4}e}, {1:{3}.{4}e}, {2:{3}.{4}e}]",
v._x, v._y, v._z, width, precision);
}
与 I/O 操作符不同,std::format
不会改变 ostream
的格式化状态,从而避免了 Bo Persson 提到的问题:
你真正的问题是这个输出之后的下一个输出会发生什么......
I/O 操纵器也能与 Vector
一起正常工作。
在 std::format
可用之前,您可以使用它所基于的 the {fmt} library。
答案 2 :(得分:1)
除了setw()之外的所有内容实际上都已经这样做了。它们是“粘性的”。
你真正的问题是在这之后的下一个输出会发生什么......
答案 3 :(得分:1)
通常,您不直接使用标准操纵器。在 例如,在这种情况下,您可以定义一个操纵器 fromVector,并使用:
output << '['
<< fromVector << v.x << ", "
<< fromVector << v.y << ", "
<< fromVector << v.z << ']';
这样,如果你想改变宽度和精度 向量中的元素,你只需要在一个地方做。
在这种情况下,操纵器没有参数,所有这些都是 需要的是一个简单的功能:
std::ostream& fromVector(std::ostream& stream)
{
stream.setf(std::ios::right, std::ios::adjustfield);
stream.setf(std::ios::scientific, std::ios::floatfield);
stream.precision(16);
stream.width(32);
return stream;
}
当然,这将改变任何格式的大部分格式 以后的用户。一般来说,使用它会更好 一个临时类对象,它保存状态并恢复它 在析构函数中。我通常派出我的操纵者 类似的东西:
头: class StateSavingManip { 上市: StateSavingManip( StateSavingManip const&amp;其他);
virtual ~StateSavingManip() ;
void operator()( std::ios& stream ) const ;
protected:
StateSavingManip() ;
private:
virtual void setState( std::ios& stream ) const = 0 ;
private:
StateSavingManip& operator=( StateSavingManip const& ) ;
private:
mutable std::ios* myStream ;
mutable std::ios::fmtflags
mySavedFlags ;
mutable int mySavedPrec ;
mutable char mySavedFill ;
} ;
inline std::ostream&
operator<<(
std::ostream& out,
StateSavingManip const&
manip )
{
manip( out ) ;
return out ;
}
inline std::istream&
operator>>(
std::istream& in,
StateSavingManip const&
manip )
{
manip( in ) ;
return in ;
}
源: int getXAlloc(); int ourXAlloc = getXAlloc()+ 1;
int
getXAlloc()
{
if ( ourXAlloc == 0 ) {
ourXAlloc = std::ios::xalloc() + 1 ;
assert( ourXAlloc != 0 ) ;
}
return ourXAlloc - 1 ;
}
}
StateSavingManip::StateSavingManip()
: myStream( NULL )
{
}
StateSavingManip::StateSavingManip(
StateSavingManip const&
other )
{
assert( other.myStream == NULL ) ;
}
StateSavingManip::~StateSavingManip()
{
if ( myStream != NULL ) {
myStream->flags( mySavedFlags ) ;
myStream->precision( mySavedPrec ) ;
myStream->fill( mySavedFill ) ;
myStream->pword( getXAlloc() ) = NULL ;
}
}
void
StateSavingManip::operator()(
std::ios& stream ) const
{
void*& backptr = stream.pword( getXAlloc() ) ;
if ( backptr == NULL ) {
backptr = const_cast< StateSavingManip* >( this ) ;
myStream = &stream ;
mySavedFlags = stream.flags() ;
mySavedPrec = stream.precision() ;
mySavedFill = stream.fill() ;
}
setState( stream ) ;
}
如果你这样做,你必须在之后添加括号 操纵者,例如:
output << '['
<< fromVector() << v.x << ", "
<< fromVector() << v.y << ", "
<< fromVector() << v.z << ']';
(我确信一些聪明的灵魂会找到一种方法 避开他们,但他们从来没有打扰过我,所以我没有 打扰。)