Какие манипуляторы iomanip являются "липкими"?
недавно у меня возникла проблема с созданием 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()? - наконец, есть ли онлайн-ссылка, которая четко документирует это поведение? Моя документация поставщика (MS Visual Studio 2005), похоже, не ясно показывает это.
3 ответов:
важные заметки из комментариев ниже:
Мартин:
@Chareles: тогда по этому требованию все манипуляторы липкие. Кроме setw, который, кажется, сбрасывается после использования.
Чарльз:
точно! и единственная причина, по которой setw ведет себя по-разному, заключается в том, что существуют требования к операциям форматированного вывода явно .ширина(0) выход поток.
ниже приводится обсуждение, которое приводит к вышеуказанному выводу:
глядя на код, следующие манипуляторы возвращают объект, а не поток:
setiosflags resetiosflags setbase setfill setprecision setwэто распространенный метод для применения операции только к следующему объекту, который применяется к потоку. К сожалению, это не мешает им быть липким. Тесты показывают, что все они, кроме
setwлипкие.setiosflags: Sticky resetiosflags:Sticky setbase: Sticky setfill: Sticky setprecision: Stickyвсе другие манипуляторы возвращают объект потока. Таким образом, любая информация о состоянии, которую они изменяют, должна быть записана в потоковом объекте и, таким образом, постоянна (пока другой манипулятор не изменит состояние). Таким образом, следующие манипуляторы должны быть Липки манипуляторы.
[no]boolalpha [no]showbase [no]showpoint [no]showpos [no]skipws [no]unitbuf [no]uppercase dec/ hex/ oct fixed/ scientific internal/ left/ rightэти манипуляторы фактически выполняют операцию над самим потоком, а не над объектом потока (хотя технически поток является частью состояния объектов потока). Но я им не верю влияет на любую другую часть состояния объектов потока.
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не кажется "липким" является то, что некоторые операции гарантированно вызывают.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.грань.номер.класть.виртуалы]: все
do_putперегрузок дляnum_putшаблон. Они используются при перегрузкахoperator<<взявbasic_ostreamи встроенный числовой тип.22.2.6.2.2 [lib.место действия.деньги.класть.виртуалы]: все
do_putперегрузок дляmoney_putшаблон.27.6.2.5.4 [lib.острие.вставки.характер]: перегрузки
operator<<взявbasic_ostreamи один из типов char экземпляра basic_ostream илиchar, подписанногоcharилиunsigned charили указатели на массивы этих типов char.честно говоря, я не уверен в обосновании этого, но никаких других состояний
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(). Я получил свойsetw()информация от cplusplus.com.вы можете найти полный список манипуляторы здесь. Из этой ссылки все флаги потока должны быть установлены до тех пор, пока их не изменит другой манипулятор. Одна заметка о
left,rightиinternalманипуляторы: они похожи на другие флаги и do сохраняться до изменения. Однако они действуют только тогда, когда задана ширина потока, а ширина должна быть задана для каждой строки. Так, например,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входные и выходные манипуляторы не являются липкими и происходят только один раз, когда они используются. Параметризованные манипуляторы отличаются друг от друга, Вот краткое описание каждый:
setiosflagsпозволяет вручную установить флаги, список которых может быть fount здесь, так что это Липко.
resetiosflagsведет себя аналогичноsetiosflagsкроме того, что он сбрасывает указанные флаги.
setbaseустанавливает базу целых чисел, вставленных в поток (так что 17 в базе 16 будет "11", а в базе 2 будет "10001").
setfillустанавливает символ заливки для вставки в поток, когда это.
setprecisionзадает число десятичных знаков, используемых при вставке значений с плавающей точкой.
setwделает только следующую вставку заданной ширины путем заполнения символом, указанным вsetfill
Comments