Кто спроектировал / разработал IOStreams для C ++ и будет ли он по-прежнему считаться хорошо разработанным по сегодняшним стандартам? [закрыто]


128

Во-первых, может показаться, что я прошу субъективного мнения, но это не то, что мне нужно. Очень хотелось бы услышать аргументированные аргументы на эту тему.


В надежде получить некоторое представление о том, как должна быть разработана современная структура потоков / сериализации, я недавно купил себе копию книги « Стандартные C ++ IOStreams and Locales » Анджелики Лангер и Клауса Крефта . Я полагал, что если бы IOStreams не был хорошо спроектирован, он бы вообще не попал в стандартную библиотеку C ++.

Прочитав различные части этой книги, я начинаю сомневаться, можно ли IOStreams сравнивать, например, с STL с общей архитектурной точки зрения. Прочтите, например, это интервью с Александром Степановым («изобретателем» STL), чтобы узнать о некоторых проектных решениях, которые вошли в STL.

Что меня особенно удивляет :

  • Кажется, неизвестно, кто отвечал за общий дизайн IOStreams (я хотел бы прочитать некоторую справочную информацию об этом - кто-нибудь знает хорошие ресурсы?);

  • Как только вы погрузитесь в непосредственную поверхность IOStreams, например, если вы хотите расширить IOStreams своими собственными классами, вы попадете в интерфейс с довольно загадочными и запутанными именами функций-членов, например getloc/ imbue, uflow/ underflow, snextc/ sbumpc/ sgetc/ sgetn, pbase/ pptr/ epptr(и есть наверное даже худшие примеры). Это значительно усложняет понимание общей конструкции и взаимодействия отдельных частей. Даже книга , которую я упоминал выше , не помогает , что много (ИМХО).


Таким образом, мой вопрос:

Если бы вам пришлось судить по сегодняшним стандартам разработки программного обеспечения (если на самом деле существует какое-либо общее соглашение по ним), можно ли считать IOStreams C ++ хорошо разработанными? (Я бы не хотел улучшать свои навыки разработки программного обеспечения с помощью того, что обычно считается устаревшим.)


7
Интересное мнение Херба Саттера stackoverflow.com/questions/2485963/… :) Жаль, что парень ушел из SO после всего лишь нескольких дней участия
Йоханнес Шауб - litb

5
Есть ли еще кто-нибудь, кто видит смешение проблем в потоках STL? Поток обычно предназначен для чтения или записи байтов и ничего больше. То, что может читать или записывать определенные типы данных, - это средство форматирования (которое может, но не обязательно, использовать поток для чтения / записи форматированных байтов). Объединение обоих в один класс еще более усложняет реализацию собственных потоков.
мммммммм

4
@rsteven, эти проблемы разделены. std::streambufявляется базовым классом для чтения и записи байтов, а istream/ ostreamпредназначен для форматирования ввода и вывода, принимая указатель на std::streambufего место назначения / источник.
Йоханнес Шауб - лит

1
@litb: Но можно ли переключить streambuf, который используется потоком (форматировщиком)? Так что, может быть, я хочу использовать форматирование STL, но хочу записывать данные через определенный streambuf?
мммммммм

2
@rstevens,ostream foo(&somebuffer); foo << "huh"; foo.rdbuf(cout.rdbuf()); foo << "see me!";
Йоханнес Шауб - litb

Ответы:


31

Несколько непродуманных идей нашли свое отражение в стандарте:, auto_ptrи vector<bool>, valarrayи это exportлишь некоторые из них. Поэтому я бы не стал воспринимать наличие IOStreams как признак качественного дизайна.

У IOStreams неоднозначная история. На самом деле они представляют собой переработку более ранней библиотеки потоков, но были созданы в то время, когда многие из сегодняшних идиом C ++ не существовали, поэтому у дизайнеров не было возможности оглянуться назад. Одна проблема, которая стала очевидной только со временем, заключалась в том, что практически невозможно реализовать IOStreams так же эффективно, как stdio C, из-за обильного использования виртуальных функций и пересылки во внутренние буферные объекты даже с самой тонкой степенью детализации, а также из-за некоторой непостижимой странности. способом определения и реализации локалей. Признаюсь, я плохо помню об этом; Я помню, как несколько лет назад он был предметом интенсивных дебатов на comp.lang.c ++. Moderated.


3
Спасибо за ваш вклад. Я просматриваю comp.lang.c++.moderatedархив и размещаю ссылки внизу своего вопроса, если найду что-нибудь ценное. - Кроме того, я осмелюсь с вами не согласиться auto_ptr: после прочтения « Исключительного C ++» Херба Саттера он кажется очень полезным для реализации шаблона RAII.
stakx - больше не участвует

5
@stakx: Тем не менее, он устарел и заменяется unique_ptrболее четкой и мощной семантикой.
UncleBens

3
@UncleBens unique_ptrтребует ссылки на rvalue. Так что на данный момент auto_ptrочень мощный указатель.
Артём

7
Но auto_ptrон испортил семантику копирования / присваивания, что сделало его нишей для разыменования ошибок ...
Матье М.

5
@TokenMacGuy: это не вектор и не хранит bools. Что вводит в заблуждение. ;)
jalf

40

Что касается того, кто их разработал, оригинальная библиотека (что неудивительно) была создана Бьярном Страуструпом, а затем переопределена Дэйвом Пресотто. Затем Джерри Шварц переделал ее и заново реализовал для Cfront 2.0, используя идею манипуляторов от Эндрю Кенига. Стандартная версия библиотеки основана на этой реализации.

Источник «Дизайн и эволюция C ++», раздел 8.3.1.


3
@Neil - орех, что ты думаешь о дизайне? Судя по другим вашим ответам, многие хотели бы услышать ваше мнение ...
ДВК

1
@DVK Только что выложил свое мнение как отдельный ответ.

2
Только что нашел стенограмму интервью с Бьярном Страуструпом, в котором он упоминает некоторые фрагменты истории IOStreams : www2.research.att.com/~bs/01chinese.html (эта ссылка сейчас временно не работает, но вы можете попробовать Кеш страниц Google)
stakx - больше не вносит вклад

2
Обновленная ссылка: stroustrup.com/01chinese.html .
FrankHB

28

Если бы вам пришлось судить по сегодняшним стандартам программной инженерии (если на самом деле есть какое-либо общее соглашение по ним), можно ли считать IOStreams C ++ хорошо разработанными? (Я бы не хотел улучшать свои навыки разработки программного обеспечения с помощью того, что обычно считается устаревшим.)

Я бы сказал НЕТ по нескольким причинам:

Плохая обработка ошибок

Об условиях ошибки следует сообщать с исключениями, а не с помощью operator void*.

Анти-шаблон «объект зомби» - вот что вызывает подобные ошибки .

Плохое разделение между форматированием и вводом-выводом

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

Это также увеличивает вероятность написания таких ошибок, как:

using namespace std; // I'm lazy.
cout << hex << setw(8) << setfill('0') << x << endl;
// Oops!  Forgot to set the stream back to decimal mode.

Если бы вместо этого вы написали что-то вроде:

cout << pad(to_hex(x), 8, '0') << endl;

Не было бы битов состояния, связанных с форматированием, и никаких проблем.

Обратите внимание, что в «современных» языках, таких как Java, C # и Python, все объекты имеют функцию toString/ ToString/, __str__которая вызывается процедурами ввода-вывода. AFAIK, только C ++ делает наоборот, используя stringstreamстандартный способ преобразования в строку.

Плохая поддержка i18n

Вывод на основе Iostream разбивает строковые литералы на части.

cout << "My name is " << name << " and I am " << occupation << " from " << hometown << endl;

Строки формата помещают целые предложения в строковые литералы.

printf("My name is %s and I am %s from %s.\n", name, occupation, hometown);

Последний подход легче адаптировать к библиотекам интернационализации, таким как GNU gettext, потому что использование целых предложений дает переводчикам больше контекста. Если ваша подпрограмма форматирования строк поддерживает переупорядочение (например, параметры POSIX $printf), она также лучше справляется с различиями в порядке слов между языками.


4
Фактически, для i18n замены должны обозначаться позициями (% 1,% 2, ..), поскольку при преобразовании может потребоваться изменить порядок параметров. В остальном полностью согласен - +1.
peterchen 05

4
@peterchen: Для этого нужны $спецификаторы POSIX printf.
jamesdlin 05

2
Проблема не в форматных строках, а в том, что в C ++ нет типизированных переменных.
dan04,

5
Начиная с C ++ 11 теперь есть типизированные переменные.
Mooing Duck

2
ИМХО «дополнительная информация о состоянии» - это худшая проблема. cout является глобальным; прикрепление к нему флагов форматирования делает эти флаги глобальными, и если учесть, что в большинстве случаев их использование имеет предполагаемый объем в несколько строк, это довольно ужасно. Это можно было бы исправить с помощью класса 'formatter', который связывается с ostream, но сохраняет свое собственное состояние. И то, что делается с помощью cout, обычно выглядит ужасно по сравнению с тем же, что делается с printf (когда это возможно) ..
greggo

17

Я отправляю это как отдельный ответ, потому что это чистое мнение.

Выполнение ввода и вывода (особенно ввода) - очень и очень сложная проблема, поэтому неудивительно, что библиотека iostreams полна объектов и вещей, которые с идеальной ретроспективой можно было бы сделать лучше. Но мне кажется, что все библиотеки ввода-вывода на любом языке такие. Я никогда не использовал язык программирования, в котором система ввода-вывода была бы такой красивой вещью, которая заставляла меня трепетать перед ее создателем. Библиотека iostreams имеет преимущества, особенно по сравнению с библиотекой CI / O (расширяемость, безопасность типов и т. Д.), Но я не думаю, что кто-то считает ее примером отличного объектно-ориентированного или универсального дизайна.


16

Мое мнение о C ++ iostreams значительно улучшилось со временем, особенно после того, как я начал фактически расширять их, реализуя свои собственные классы потоков. Я начал ценить расширяемость и общий дизайн, несмотря на смехотворно плохие имена функций-членов, вроде xsputnили что-то еще. Тем не менее, я считаю, что потоки ввода-вывода являются значительным улучшением по сравнению с C stdio.h, который не имеет безопасности типов и пронизан серьезными недостатками безопасности.

Я думаю, что основная проблема с потоками ввода-вывода состоит в том, что они объединяют две взаимосвязанные, но несколько ортогональные концепции: текстовое форматирование и сериализацию. С одной стороны, потоки ввода-вывода предназначены для создания удобочитаемого форматированного текстового представления объекта, а с другой - для сериализации объекта в переносимый формат. Иногда эти две цели являются одними и теми же, но иногда это приводит к серьезным досадным несоответствиям. Например:

std::stringstream ss;
std::string output_string = "Hello world";
ss << output_string;

...

std::string input_string;
ss >> input_string;
std::cout << input_string;

Здесь то, что мы получаем в качестве ввода, не является тем, что мы изначально выводили в поток. Это связано с тем, что <<оператор выводит всю строку, тогда как >>оператор будет только читать из потока, пока не встретит пробельный символ, поскольку в потоке нет информации о длине . Таким образом, даже если мы выводим строковый объект, содержащий "hello world", мы собираемся ввести только строковый объект, содержащий "hello". Таким образом, хотя поток выполнил свою задачу как средство форматирования, ему не удалось должным образом сериализовать, а затем десериализовать объект.

Вы можете сказать, что потоки ввода-вывода не были предназначены для использования в качестве средств сериализации, но если это так, то для чего на самом деле потоки ввода ? Кроме того, на практике потоки ввода-вывода часто используются для сериализации объектов, поскольку других стандартных средств сериализации нет. Рассмотрим boost::date_timeили boost::numeric::ublas::matrix, где, если вы выводите матричный объект с помощью <<оператора, вы получите такую ​​же точную матрицу, когда вы введете ее с помощью >>оператора. Но для этого разработчикам Boost приходилось хранить информацию о количестве столбцов и строк в виде текстовых данных на выходе, что ставит под угрозу фактическое удобочитаемое отображение. Опять же, неудобное сочетание возможностей текстового форматирования и сериализации.

Обратите внимание, как большинство других языков разделяют эти два средства. В Java, например, форматирование выполняется с помощью toString()метода, а сериализация - с помощью Serializableинтерфейса.

На мой взгляд, лучшим решением было бы ввести потоки на основе байтов наряду со стандартными потоками на основе символов . Эти потоки будут работать с двоичными данными, не заботясь о удобочитаемом форматировании / отображении. Их можно использовать исключительно как средства сериализации / десериализации для преобразования объектов C ++ в переносимые последовательности байтов.


Спасибо за ответы. Я вполне мог ошибаться в этом, но что касается вашего последнего пункта (побайтовые и символьные потоки), разве IOStream (частичный?) Не отвечает на это разделение между буферами потока (преобразование символов, транспорт и буферизация) а потоки (форматирование / парсинг)? И не могли бы вы создать новые классы потоков, те, которые предназначены исключительно для (машиночитаемой) сериализации и десериализации, а другие, которые однозначно предназначены для (удобочитаемого) форматирования и синтаксического анализа?
stakx - больше не вносит свой вклад

@stakx, да, собственно, я это сделал. Это немного раздражает больше, чем кажется, поскольку std::char_traitsне может быть портативно специализированным, чтобы взять unsigned char. Однако есть обходные пути, поэтому я думаю, что расширяемость снова приходит на помощь. Но я думаю, что тот факт, что потоки на основе байтов нестандартны, является слабым местом библиотеки.
Чарльз Сальвия

4
Кроме того, реализация двоичных потоков требует, чтобы вы реализовали новые классы потоков и новые классы буферов, поскольку задачи форматирования не полностью отделены от std::streambuf. Итак, единственное, что вы расширяете, - это std::basic_iosкласс. Итак, есть линия, в которой «расширение» переходит в территорию «полного переопределения», и создание двоичного потока из средств потока ввода-вывода C ++, кажется, приближается к этой точке.
Чарльз Сальвия,

хорошо сказано и именно то, что я подозревал. И тот факт, что и C, и C ++ делают все возможное, чтобы не давать гарантий относительно конкретной разрядности и представлений, действительно может стать проблематичным, когда дело доходит до выполнения ввода-вывода.
stakx - больше не вносит свой вклад

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

11

Мне всегда казалось, что C ++ IOStreams плохо спроектированы: их реализация очень затрудняет правильное определение нового типа потока. они также смешивают функции io и функции форматирования (подумайте о манипуляторах).

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

Я бы хотел, чтобы С ++ был таким же простым в отношении потоков ...


В упомянутой мной книге объясняется базовая архитектура IOStreams следующим образом: существует транспортный уровень (классы буфера потока) и уровень синтаксического анализа / форматирования (классы потоков). Первые отвечают за чтение / запись символов из / в байтовый поток, а вторые - за синтаксический анализ символов или сериализацию значений в символы. Это кажется достаточно ясным, но я не уверен, действительно ли эти проблемы четко разделены в реальности, особенно. когда в игру вступают локации. - Я также согласен с вами в том, что сложно реализовать новые классы потоков.
stakx - больше не участвует

"сочетание функций io и функций форматирования" <- Что в этом плохого? Вот в чем смысл библиотеки. Что касается создания новых потоков, вы должны создать streambuf вместо потока и построить простой поток вокруг streambuf.
Билли Онил

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

@stakx: Если бы слой streambuf сделал то, что вы сказали, все было бы хорошо. Но преобразование между символьной последовательностью и байтом все смешано с фактическим вводом-выводом (файл, консоль и т. Д.). Невозможно выполнить файловый ввод-вывод, не выполнив также преобразование символов, что очень прискорбно.
Ben Voigt

10

Я считаю, что дизайн IOStreams великолепен с точки зрения расширяемости и полезности.

  1. Буферы потоков: ознакомьтесь с расширениями boost.iostream: создавайте gzip, tee, копируйте потоки в несколько строк, создавайте специальные фильтры и так далее. Без него это было бы невозможно.
  2. Интеграция локализации и интеграции форматирования. Посмотрите, что можно сделать:

    std::cout << as::spellout << 100 << std::endl;

    Можно напечатать: «сто» или даже:

    std::cout << translate("Good morning")  << std::endl;

    Может печатать "Bonjour" или "בוקר טוב" в зависимости от региона std::cout!

    Такое возможно только потому, что iostreams очень гибкие.

Можно ли сделать лучше?

Конечно могло! На самом деле есть много вещей, которые можно улучшить ...

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

Но, оглядываясь назад много лет назад, я по-прежнему считаю, что дизайн библиотеки был достаточно хорош, чтобы принести много полезного.

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


5
Не могли бы вы прокомментировать, почему ваши примеры для пункта 2 лучше, чем просто использовать что-то вроде print (spellout(100));и. print (translate("Good morning"));Это могло бы показаться хорошей идеей, поскольку это отделяет форматирование и i18n от ввода-вывода.
Schedler

3
Потому что это можно перевести на язык, вложенный в поток. т.е.: french_output << translate("Good morning"); english_output << translate("Good morning") даст вам: "Bonjour Good morning"
Артём

3
Локализация намного сложнее, когда вам нужно сделать '<< "text" << value' на одном языке, но '<< value << "text"' на другом - по сравнению с printf
Мартин Беккет

@Martin Beckett Я знаю, взгляните на библиотеку Boost.Locale, что происходит, что в таком случае вы делаете, out << format("text {1}") % valueи это может быть переведено "{1} translated". Так что работает нормально ;-).
Артем

15
То, что «можно сделать», не очень важно. Вы программист, все можно сделать , приложив достаточно усилий. Но IOStreams делает очень болезненным достижение большей части того, что можно сделать . И обычно за свои хлопоты вы получаете паршивую производительность.
jalf

2

(Этот ответ основан только на моем мнении)

Я думаю, что потоки IOStream намного сложнее, чем их функциональные эквиваленты. Когда я пишу на C ++, я все еще использую заголовки cstdio для ввода-вывода «старого стиля», который я считаю гораздо более предсказуемым. Кстати, (хотя это на самом деле не важно; абсолютная разница во времени незначительна) IOStreams во многих случаях было доказано, что они медленнее, чем CI / O.


Я думаю, вы имеете в виду «функцию», а не «функциональность». функциональное программирование создает код, который выглядит еще хуже, чем обычное программирование.
Крис Бек,

Спасибо, что указали на эту ошибку; Я отредактировал ответ, чтобы отразить исправление.
Делан Азабани

5
IOStreams почти наверняка должен быть медленнее, чем классический stdio; Если бы мне дали задачу разработать расширяемую и простую в использовании структуру потоков ввода-вывода, я бы, вероятно, оценил скорость второстепенно, учитывая, что реальными узкими местами, скорее всего, будут скорость файлового ввода-вывода или пропускная способность сетевого трафика.
stakx - больше не участвует

1
Я согласен, что для ввода-вывода или сети скорость вычислений не имеет большого значения. Однако помните, что C ++ для преобразования чисел / строк использует sstringstream. Думаю, скорость имеет значение, хотя это вторично.
Matthieu M.

1
Файловый ввод-вывод @stakx и узкие места в сети зависят от затрат «на байт», которые довольно малы и резко снижаются за счет технологических усовершенствований. Кроме того, учитывая DMA, эти накладные расходы не отнимают время процессора у других потоков на той же машине. Итак, если вы выполняете форматированный вывод, затраты на то, чтобы сделать это эффективно, а не нет, могут легко быть значительными (по крайней мере, не затмеваются диском или сетью; более вероятно, что они затмеваются другой обработкой в ​​приложении).
greggo

2

Я всегда сталкиваюсь с неожиданностями при использовании IOStream.

Библиотека кажется текстовой, а не двоичной. Это может быть первым сюрпризом: использование двоичного флага в файловых потоках недостаточно для получения двоичного поведения. Пользователь Charles Salvia, приведенный выше, заметил это правильно: IOStreams смешивает аспекты форматирования (где вам нужен красивый вывод, например, ограниченные цифры для чисел с плавающей запятой) с аспектами сериализации (где вы не хотите потери информации). Наверное, было бы хорошо разделить эти аспекты. Boost.Serialization делает эту половину. У вас есть функция сериализации, которая при желании направляет средства вставки и экстракторы. Между обоими аспектами уже есть напряжение.

Многие функции также имеют запутанную семантику (например, get, getline, ignore и read. Некоторые извлекают разделитель, некоторые нет; также некоторые устанавливают eof). Далее некоторые упоминают странные имена функций при реализации потока (например, xsputn, uflow, underflow). Ситуация становится еще хуже, если использовать варианты wchar_t. Wifstream выполняет преобразование в многобайтовый формат, а wstringstream - нет. Двоичный ввод-вывод не работает из коробки с wchar_t: у вас есть перезапись codecvt.

Буферизованный ввод-вывод c (то есть FILE) не так мощен, как его аналог C ++, но более прозрачен и имеет гораздо менее интуитивно понятное поведение.

Тем не менее, каждый раз, когда я натыкаюсь на IOStream, он меня привлекает, как мотылька, чтобы выстрелить. Наверное, было бы хорошо, если бы какой-нибудь действительно умный парень хорошо разбирался в архитектуре в целом.


1

Не могу не ответить на первую часть вопроса (Кто это сделал?). Но ответ был дан в других сообщениях.

Что касается второй части вопроса (Хорошо продуман?), То я отвечу категоричным «Нет!». Вот небольшой пример, который заставляет меня годами недоверчиво качать головой:

#include <stdint.h>
#include <iostream>
#include <vector>

// A small attempt in generic programming ;)
template <class _T>
void ShowVector( const char *title, const std::vector<_T> &v)
{
    std::vector<_T>::const_iterator iter;
    std::cout << title << " (" << v.size() << " elements): ";
    for( iter = v.begin(); iter != v.end(); ++iter )
    {
        std::cout << (*iter) << " ";
    }
    std::cout << std::endl;
}
int main( int argc, const char * argv[] )
{
    std::vector<uint8_t> byteVector;
    std::vector<uint16_t> wordVector;
    byteVector.push_back( 42 );
    wordVector.push_back( 42 );
    ShowVector( "Garbled bytes as characters output o.O", byteVector );
    ShowVector( "With words, the numbers show as numbers.", wordVector );
    return 0;
}

Приведенный выше код производит ерунду из-за дизайна iostream. По некоторым причинам, не зависящим от меня, они обрабатывают байты uint8_t как символы, тогда как более крупные целочисленные типы обрабатываются как числа. Qed Плохой дизайн.

Я также не могу придумать, как это исправить. Тип также может быть float или double ... поэтому приведение к int, чтобы заставить глупый iostream понять, что числа, а не символы, не помогут.

После того, как мой ответ проголосовал против, возможно, еще несколько слов объяснения ... Дизайн IOStream ошибочен, поскольку он не дает программисту возможности заявить, КАК обрабатывается элемент. Реализация IOStream принимает произвольные решения (например, рассматривает uint8_t как символ, а не как число байта). Это недостаток дизайна IOStream, поскольку они пытаются достичь недостижимого.

C ++ не позволяет классифицировать тип - язык не имеет возможности. Не существует таких вещей, как is_number_type () или is_character_type (), которые IOStream мог бы использовать для разумного автоматического выбора. Игнорирование этого и попытка уйти от угадывания ЯВЛЯЕТСЯ недостатком дизайна библиотеки.

Допустим, printf () также не сможет работать в универсальной реализации ShowVector (). Но это не оправдание поведению iostream. Но очень вероятно, что в случае printf () ShowVector () будет определен следующим образом:

template <class _T>
void ShowVector( const char *formatString, const char *title, const std::vector<_T> &v );

3
Виновата (чисто) не iostream. Проверьте , что ваш uint8_tявляется ЬурейеЙ для. Это на самом деле чугун? Тогда не обвиняйте iostreams в том, что они относятся к нему как к чучелу.
Мартин Ба,

И если вы хотите убедиться, что вы получаете число в универсальном коде, вы можете использовать num_putфасет вместо оператора вставки потока.
Мартин Ба,

@Martin Ba. Вы правы - стандарты c / c ++ не раскрывают, сколько байтов имеет "short unsigned int". «unsigned char» - это идиосинкразия языка. Если вам действительно нужен байт, вы должны использовать беззнаковый символ. C ++ также не позволяет накладывать ограничения на аргументы шаблона, такие как «только числа», и поэтому, если я изменил реализацию ShowVector на предложенное вами решение num_put, ShowVector больше не смог бы отображать вектор строк, верно? ;)
BitTickler

1
@Martin Bla: cppreference упоминает, что int8_t - это целочисленный тип со знаком с шириной ровно 8 бит. Я согласен с автором, что странно, что вы получаете вывод мусора, хотя это технически объясняется typedef и перегрузкой типов char в iostream , Это могло быть решено, если __int8 был истинным типом вместо typedef.
gast128

О, на самом деле это довольно легко исправить: // Исправления для std :: ostream, который нарушил поддержку беззнаковых / подписанных / типов символов // и печатает 8-битные целые числа, как будто они были символами. пространство имен ostream_fixes {встроенный std :: ostream & operator << (std :: ostream & os, unsigned char i) {return os << static_cast <unsigned int> (i); } встроенный std :: ostream & operator << (std :: ostream & os, signed char i) {return os << static_cast <signed int> (i); }} // пространство имен ostream_fixes
mcv 05

1

Как отмечалось в других ответах, у iostreams C ++ много недостатков, но я хотел бы отметить кое-что в его защиту.

C ++ практически уникален среди серьезно используемых языков, что упрощает ввод и вывод переменных для начинающих. В других языках пользовательский ввод обычно включает в себя приведение типов или средства форматирования строк, в то время как C ++ заставляет компилятор выполнять всю работу. То же самое в основном верно и для вывода, хотя C ++ в этом отношении не так уникален. Тем не менее, вы можете довольно хорошо выполнять форматированный ввод-вывод на C ++ без необходимости понимать классы и объектно-ориентированные концепции, что полезно с педагогической точки зрения, и без необходимости понимать синтаксис формата. Опять же, если вы обучаете новичков, это большой плюс.

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

Используя наш сайт, вы подтверждаете, что прочитали и поняли нашу Политику в отношении файлов cookie и Политику конфиденциальности.
Licensed under cc by-sa 3.0 with attribution required.