Я создал класс 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
), чтобы вы могли сказать что-то вроде "печатать символы стандартным образом, но цифры с этим заданным форматом".
Спасибо!
Изменить
Согласно комментарию Роба Адамса, я изменил свой уродливый код (который, как указывали другие, испортил бы точность для "следующего парня" ) более кратким (и правильным):
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;
}
Ответ 1
Только 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;
}
См. также этот другой вопрос, где они решили, что вы не можете сделать ширину постоянной.
Ответ 2
Все, кроме setw(), действительно делают это уже. Они "липкие".
Ваша настоящая проблема - это то, что происходит со следующим выходом после этого...
Ответ 3
Как правило, вы не используете стандартные манипуляторы напрямую. В
в этом случае, например, вы можете определить манипулятор
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 & другое);
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 << ']';
(Я уверен, что какая-то умная душа найдет способ
избегая их, но они меня никогда не беспокоили, поэтому я не
беспокоили.)