Недавно у меня возникла проблема с созданием stringstream
из-за того, что я ошибочно предположил, std::setw()
что это повлияет на поток строк при каждой вставке, пока я не изменил его явно. Однако после вставки он всегда сбрасывается.
// 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), похоже, не ясно показывает это.
Ответы:
Важные примечания из комментариев ниже:
Автор: Мартин:
Чарльз:
Следующее обсуждение приводит к вышеуказанному выводу:
Глядя на код, следующие манипуляторы возвращают объект, а не поток:
Это распространенный метод применения операции только к следующему объекту, который применяется к потоку. К сожалению, это не мешает им быть липкими. Тесты показывают, что все они, кроме
setw
липких.Все остальные манипуляторы возвращают объект потока. Таким образом, любая информация о состоянии, которую они изменяют, должна быть записана в объект потока и, таким образом, является постоянной (пока другой манипулятор не изменит состояние). Таким образом, следующие манипуляторы должны быть липкими манипуляторами.
Эти манипуляторы фактически выполняют операцию с самим потоком, а не с объектом потока (хотя технически поток является частью состояния объектов потока). Но я не верю, что они влияют на другие части состояния объектов потока.
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
источник
operator<<
манипулятору гарантирует, что состояние потока будет изменено определенным образом. Ни одна из форм не устанавливает никакого государственного караула. Только поведение следующей операции форматированной вставки определяет, какая часть состояния сбрасывается, если таковая имеется.setw
кажется, ведет себя иначе, состоит в том, что существуют требования к операциям форматированного вывода для явного.width(0)
вывода потока вывода.Причина,
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.facet.num.put.virtuals]: все
do_put
перегрузки дляnum_put
шаблона. Они используются перегрузками дляoperator<<
полученияbasic_ostream
и встроенного числового типа.22.2.6.2.2 [lib.locale.money.put.virtuals]: все
do_put
перегрузки дляmoney_put
шаблона.27.6.2.5.4 [lib.ostream.inserters.character]: перегрузки
operator<<
принимаяbasic_ostream
и один из символьного типа в basic_ostream конкретизации илиchar
, подписанныйchar
илиunsigned 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
манипуляторы: Они похожи на другие флаги и сделать сохраняться , пока не изменились. Однако они действуют только в том случае, если задана ширина потока, а ширина должна задаваться для каждой строки. Так, напримерcout.width(6); cout << right << "a" << endl; cout.width(6); cout << "b" << endl; cout.width(6); cout << "c" << endl;
дал бы вам
но
cout.width(6); cout << right << "a" << endl; cout << "b" << endl; cout << "c" << endl;
дал бы вам
Манипуляторы ввода и вывода не являются «липкими» и появляются только один раз там, где они используются. Все параметризованные манипуляторы отличаются друг от друга, вот краткое описание каждого из них:
setiosflags
позволяет вам вручную устанавливать флаги, список которых можно найти здесь , так что он липкий.resetiosflags
ведет себя аналогично, заsetiosflags
исключением того, что снимает указанные флаги.setbase
устанавливает базу целых чисел, вставленных в поток (таким образом, 17 в базе 16 будет «11», а в базе 2 будет «10001»).setfill
устанавливает символ заполнения для вставки в поток приsetw
использовании.setprecision
устанавливает десятичную точность, которая будет использоваться при вставке значений с плавающей запятой.setw
делает только следующую вставку указанной ширины, заполняя символом, указанным вsetfill
источник
std::hex
и не липкие и, очевидно,std::flush
илиstd::setiosflags
не липкий либо. Так что я не думаю, что это так просто.std::hex
что не является липкой, была неправильной - я тоже только что узнал об этом. Флаги потока, однако, могут измениться, даже если вы не вставитеstd::setiosflags
снова, поэтому это можно будет увидеть как неприлипающее. Кроме того,std::ws
не липнет. Так что это не что легко.