Какие манипуляторы 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

Другие вопросы по тегам