Использование stringstream в функции ostream

Я смотрю в обеспечение ostream операторы для некоторых математических классов (матрица, вектор и т. д.) Друг заметил, что реализация стандартной библиотеки gcc ostream оператор для std::complex включает внутреннее использование строкового потока для форматирования вывода перед передачей его фактическому ostream:

///  Insertion operator for complex values.
template<typename _Tp, typename _CharT, class _Traits>
basic_ostream<_CharT, _Traits>&
operator<<(basic_ostream<_CharT, _Traits>& __os, const complex<_Tp>& __x)
{
basic_ostringstream<_CharT, _Traits> __s;
__s.flags(__os.flags());
__s.imbue(__os.getloc());
__s.precision(__os.precision());
__s << '(' << __x.real() << ',' << __x.imag() << ')';
return __os << __s.str();
}

Этот паттерн виден и в бусте. Мы пытаемся определить, стоит ли следовать этой схеме. Были опасения, что это включает в себя добавление дополнительного заголовка для потока строк, и в потоке строк требуются дополнительные выделения кучи, которых потенциально можно избежать.

Наиболее разумно было предположить, что, если клиенту требуется эта функциональность, он может создать поток строк и выполнить предварительную передачу самостоятельно.

Может ли кто-нибудь помочь мне понять, почему это будет считаться хорошей практикой и должен ли я ее принимать?

8

Решение

Подумайте, что произойдет, если вы установите выходную ширину в ostream, а затем запишите в нее std :: complex — вы не хотите, чтобы ширина влияла только на первую операцию вывода (т.е. '(' персонаж)

std::complex i(0, 1);
std::cout << std::setw(10) << std::left << i;

Это должно напечатать "(0,1)     " не "(         0,1)"

Формируя весь вывод в виде одной строки, затем выписывая его, вывод учитывает ширину поля и другие флаги форматирования, установленные в потоке.

6

Другие решения

Причина потока, указанная в другом ответе, на самом деле не сработает: строка все еще может быть разделена на уровне буфера потока, потому что эти операции не являются атомарными при вызове из нескольких потоков.

Однако есть два соображения, которые имеют отношение к делу:

  1. Для определенных выходов вы хотите временно изменить настройки флага формата. Например, вы хотите убедиться, что определенная строка появляется с использованием шестнадцатеричной записи, для другой записи и хотите восстановить поток в исходное состояние.
  2. Что еще более важно, значение вывода width() количество символов, которое должна занимать вся строка форматирования. Если вы используете операторы вывода внутренне для другого оператора вывода, вы бы сделали первый элемент, занимающий ширину, а не результирующую всю строку, состоящую из нескольких компонентов. Например, для комплексного числа реальный элемент будет занимать width() а не сочетание реального элемента, запятой и воображаемого элемента.
4

Одна из основных целей этого шаблона — избежать сохранения манипуляторов / флагов исходного потока и сброса их перед возвратом. Boost.IoStateSavers избавляет от необходимости в этом, поэтому я бы сказал, что использование указанной библиотеки было бы лучше.

2
По вопросам рекламы [email protected]