Какие манипуляторы iomanip являются "липкими"?
У меня недавно была проблема с созданием 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), кажется, не показывает это.
3 ответа
Важные заметки из комментариев ниже:
Мартин:
@Chareles: Тогда по этому требованию все манипуляторы являются липкими. За исключением setw, который, кажется, сбрасывается после использования.
Чарльз:
Именно так! и единственная причина, по которой setw, по-видимому, ведет себя по-разному, состоит в том, что существуют требования к форматированным операциям вывода, чтобы явно.width(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.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
и один из типа char экземпляра 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()
информация от http://www.cplusplus.com/.
Вы можете найти полный список манипуляторов здесь. По этой ссылке все флаги потока должны указывать set, пока не будут изменены другим манипулятором. Одна заметка о left
, right
а также internal
манипуляторы: они похожи на другие флаги и сохраняются до тех пор, пока не будут изменены. Однако они имеют эффект только тогда, когда ширина потока установлена, и ширина должна быть установлена в каждой строке. Так, например
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
позволяет вам вручную устанавливать флаги, список которых можно найти здесь, так что он липкий.
resetiosflags
ведет себя аналогично setiosflags
за исключением того, что снимает указанные флаги.
setbase
устанавливает базу целых чисел, вставленных в поток (поэтому 17 в базе 16 будет "11", а в базе 2 будет "10001").
setfill
устанавливает символ заливки для вставки в поток, когда setw
используется.
setprecision
устанавливает десятичную точность, которая будет использоваться при вставке значений с плавающей запятой.
setw
делает только следующую вставку указанной ширины, заполняя символ, указанный в setfill