Какие манипуляторы 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), похоже, не ясно показывает это.

723   3  

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

    Ничего не найдено.