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

все другие манипуляторы возвращают объект stream. Таким образом, любая информация о состоянии, которую они изменяют, должна быть записана в объект потока и, таким образом, является постоянной (до тех пор, пока другой манипулятор не изменит состояние). Таким образом, следующие манипуляторы должны быть Липки манипуляторы.

[no]boolalpha
[no]showbase
[no]showpoint
[no]showpos
[no]skipws
[no]unitbuf
[no]uppercase

dec/ hex/ oct

fixed/ scientific

internal/ left/ right

эти манипуляторы фактически выполняют операцию над самим потоком, а не объектом потока (хотя технически поток является частью состояния объектов потока). Но я не верю им. влияет на любую другую часть состояния объектов stream.

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.струна.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.острим.вставки.character]: перегрузки 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() влияет только на следующей вставки. Вот именно так!--4--> ведет себя. Поведение setw() это то же самое, что ios_base::width(). У меня setw() информация от cplusplus.com.

вы можете найти полный список манипуляторы здесь. Из этой ссылки все флаги потока должны говорить set, пока не будут изменены другим манипулятором. Одна заметка о 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