LINUX.ORG.RU

Производительность C++

 ,


7

13

Как насчёт производительности у C++ по сравнению с C? Мои предположения на текущий момент:

1) Код, не использующий возможности C++ (то есть по сути plain C), скомпилированный C++ компилятором будет иметь абсолютно ту же производительность, что и код на С.

2) Исключения и dynamic_cast медленные. Если нам важна производительность, лучше их не использовать.

3) Класс без виртуальных методов, должен быть по производительности эквивалентен сишной структуре и функциям, обрабатывающим её. Не считая копирования. Нужно везде, где можно использовать передачу по указателю или ссылке (собственно, если в Си делать memmove при передаче структуры в качестве аргумента, то это вызовет примерно такой же оверхед, как дефолтный конструктор копирования С++. Верно?).

4) Класс с виртуальными методами полностью аналогичен пункту 3, но при вызове виртуальных методов добавляется небольшой оверхед. Сишный эквивалент obj->vtable->func(obj, ...). Если сравнивать с plain C кодом, реализующим ООП в той же манере (каждая структура-объект имеет поле, указывающее на структуру, содержащую адреса функций работы с ней), то оверхеда по сравнению с plain C не должно быть.

5) При использовании атрибута класса final (если компилятор поддерживает соответствующий стандарт) даже при наличии виртуальных методов в нём, их вызов будет превращаться в прямые вызовы функций вместо обращения к vtable, если переменная имеет соответствующий тип, а не указатель/ссылка на его предка (который не final).

6) Шаблоны могут привести к разбуханию кода. Впрочем, #define-ы и inline-функции в C++ могут устроить то же самое. Вопрос: будет ли использование шаблона с одинаковыми параметрами создавать 2 копии реализации или же всё-таки компилятор догадается сделать её лишь один раз. А если шаблон используется с одинаковыми параметрами в нескольких объектных файлах? Будет ли реализация расшариваться между ними или у каждого своя?

7) Что насчёт inline-методов класса? (те, которые описываются прямо в момент определения самого класса, внутри блока class). Может ли их реализация расшариваться между модулями или в каждом будет своя копия (допустим, метод слишком длинный, чтобы инлайнится в момент вызова)?

Я не претендую на правоту, какие-то утверждения могут быть ложными. Хотел бы узнать, как обстоят дела на самом деле. А также какие подводные камни я ещё не знаю. Разумеется, речь идёт о последних версиях gcc/clang с включённой оптимизацией не ниже -O2.

★★★★★

Последнее исправление: KivApple (всего исправлений: 1)
Ответ на: комментарий от anonymous

Error смайлика для ошибок вида «неправильно составленная строка JSON» или «в строке JSON присутстуют недопустимые символы».

Поэтому возврат экземпляров смайликовского Error — это говно. У них есть проблемы, которых нет у std::string.

Лучше - это структурированный error.

Именно. Но ведь смайлик этого не показал.

Лучше с т.з. типовой безопасности.

Не понимаю. Покажите кейс, где смайликовские варианты будут типобезопаснее, чем string.

Фундаментальная проблема - это отказ от исключений.

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

Ну озвучь уже наконец для каких это «других вещей».

Мне от этого какая выгода?

eao197 ★★★★★
()
Ответ на: комментарий от anonymous

На твой взгляд.

Мой взгляд подтвержден перечислением реальных проблем и замерами производительности. Попробуйте опровергнуть чему-нибудь подобным.

eao197 ★★★★★
()
Ответ на: комментарий от eao197

Поэтому возврат экземпляров смайликовского Error — это говно.

На твой взгляд. Кому-то было бы достаточно просто факта ошибки с общим описанием без конкретики о позициях.

У них есть проблемы, которых нет у std::string.
Не понимаю. Покажите кейс, где смайликовские варианты будут типобезопаснее, чем string.
Се ля ви. Поэтому библиотеки пишутся либо под кейсы без исключений, либо под кейсы с исключениями. Поддержка и тех, и других сценариев есть далеко не везде.

Пусть, например, библиотеку вроде picojson ещё можно собрать и с поддержкой исключений. При этом API с исключениями использует API без исключений. Теперь сравни:

void throw_error(std::string&&);
и
void throw_error(Json::Error&&);
В 1-ю ф-ю можно передать любой string. Во 2-ю - только Json::Error. Включается в дело аппарат типов, нельзя случайно передать «» в throw_error. А ещё throw_error можно перегружать разными типами ошибок, мало ли для чего.

Мне от этого какая выгода?

Зачем тогда вообще общаться?

anonymous
()
Ответ на: комментарий от eao197

Мой взгляд подтвержден перечислением реальных проблем и замерами производительности.

Это вовсе не означает, что другим это должно быть интересно.

Попробуйте опровергнуть чему-нибудь подобным.

Это никому не будет интересно.

anonymous
()
Ответ на: комментарий от anonymous

На твой взгляд. Кому-то было бы достаточно просто факта ошибки с общим описанием без конкретики о позициях.

Нет. Есть библиотека, которая может выдавать динамически формируемые сообщения. Есть заявления анонима о том, что он может сделать лучше. По факту лучше не получается. Более того, приходится удалять часть уже имеющейся в библиотеки функциональности (отказываться от динамически сформированных описаний). И, плюс к тому, нужно соблюдать осторожность при работе со смайликовским классом Error:

- нельзя туда передавать указатель на динамически созданную строку;

- нельзя туда передавать указатель на временный буфер.

Все это грабли, которые возникают из-за неправильного использования C++.

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

Пусть, например, библиотеку вроде picojson ещё можно собрать и с поддержкой исключений.

А если бы у бабушки...

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

Отсюда мораль: язык C++ позволяет делать нормальные типобезопасные и нересурсоемкие решения. То, что эти решения где-то не применяются — это следствие либо принятых разработчиком проектных решений (см. на picojson в котором возвращается string, в которым автор не счел нужным в крохотной библиотеке выстраивать дополнительные типы), либо врожденного дебилизма (см. смайликовский Error).

Зачем тогда вообще общаться?

Общаться и обучать — это несколько разные вещи.

eao197 ★★★★★
()
Ответ на: комментарий от anonymous

Это вовсе не означает, что другим это должно быть интересно.

Объективные критерии не интересны? Ну okay.

eao197 ★★★★★
()
Ответ на: комментарий от AnonCxx

Попробуй мне сделать call(f, ... args) {f(args);}, где f - шаблонная функция.

Вся «проблема» в том, что придётся явно типы для шаблонной функции при вызове указать?

DarkEld3r ★★★★★
()
Ответ на: комментарий от eao197

не надоело кормить явного неадеквата? Или это спортивный интерес с целью «нащупать дно»?

anonymous
()
Ответ на: комментарий от anonymous

Опыт показывает, что после таких holy wars очень просто общаться с эффективными менеджерами. Некоторые из них ведут себя очень похожим образом.

Так что скорее это прокачивание определенных скилов :)

eao197 ★★★★★
()
Ответ на: комментарий от eao197

Есть библиотека, которая может выдавать динамически формируемые сообщения. Есть заявления анонима о том, что он может сделать лучше. По факту лучше не получается.

Есть библиотека, в которой применяется специфический и очень спорный подход в части возвращемого значения parse(). Смайлик пытался показать порочность этого подхода. Ну а то, что он не сделал в качестве члена данных string, а сделал const char*, можно с очень большой натяжкой назвать то, что он не сделал лучше. Да, динамические строки ошибок Error смайлика не может.

Все это грабли, которые возникают из-за неправильного использования C++.

Обсуждать понятие «неправильного использования C++» можно бесконечно, в данном случае, начинать надо с отказа от исключений и создания типов данных пользования. Я не вижу «неправильного использования C++» в классе Error смайлика. Используется так, как используется. Просто «нужно соблюдать осторожность при работе со смайликовским классом Error», твоим же словами.

Простейший набросок класса, который не имеет проблем смайликовского Error и накладных расходов runtime_error я уже показывал.

Твой набросок отличается от смайликовского только тем, что в качестве члена данных у тебя string, а не const char*. Ничего принципиально иного ты не показал.

либо врожденного дебилизма (см. смайликовский Error).

Это уже звучит с твоей стороны как самозащита на пустом месте.

Общаться и обучать — это несколько разные вещи.

Так я и не прошу меня обучать. Я сказал, что success() и error_desc() в твоём классе нужно объявить noexcept потому что они не генерируют исключений. И не важно, что API позиционируется для использования в условиях отсутствия исключений, и, как следствие, отсутствия try/catch и отсутствия веток выполнения обработки исключений. Важно показать пользователю в сигнатуре максимум информации.

anonymous
()
Ответ на: комментарий от anonymous

Есть библиотека, в которой применяется специфический и очень спорный подход в части возвращемого значения parse().

Именно так. Причем тут очень бы не помешало учесть мнение автора библиотеки и сценарии, под которые библиотека разрабатывалась. Возможно, термин «очень спорный» не следует использовать, а нужно оставить только «специфический».

Ну а то, что он не сделал в качестве члена данных string, а сделал const char*, можно с очень большой натяжкой назвать то, что он не сделал лучше.

Ровно до тех пор, пока кто-нибудь не наступит на эти грабли. Так что это не натяжка, это именно что грабли.

Да, динамические строки ошибок Error смайлика не может.

О том и речь.

Твой набросок отличается от смайликовского только тем, что в качестве члена данных у тебя string, а не const char*. Ничего принципиально иного ты не показал.

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

Это уже звучит с твоей стороны как самозащита на пустом месте.

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

Я сказал, что success() и error_desc() в твоём классе нужно объявить noexcept потому что они не генерируют исключений.

noexcept нужно ставить тогда, когда принципиально важно, что методы не генерируют исключений. Например, если это move-operator или функция swap. В остальных случаях все не так однозначно. И если по поводу success еще можно согласиться с использованием noexcept, то с error_desc все не так просто.

eao197 ★★★★★
()
Ответ на: комментарий от eao197

Ровно до тех пор, пока кто-нибудь не наступит на эти грабли. Так что это не натяжка, это именно что грабли.

Так пусть не наступают. Это не проблемы смайлика.

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

Концептуальной проблемы в Error смайлика нет. Есть потенциальная проблема, если какой-то лапух передаст в его Error динамически созданный буфер. А не надо передавать.

anonymous
()
Ответ на: комментарий от anonymous

Концептуальной проблемы в Error смайлика нет. Есть потенциальная проблема, если какой-то лапух передаст в его Error динамически созданный буфер. А не надо передавать.

Т.е. все просто: не надо делать ошибок в программе?

Сколько вам лет, уважаемый?

eao197 ★★★★★
()
Ответ на: комментарий от anonymous

где в качестве ошибки возвращается объекта класса, вообще для этого не предназначенный :-)

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

DarkEld3r ★★★★★
()
Ответ на: комментарий от eao197

Т.е. все просто: не надо делать ошибок в программе?

C++ ошибок не прощает. Да и ты сам говорил, «это C++, надо хоть немного знать».

Сколько вам лет, уважаемый?

Намёк ясен. У меня есть опыт достаточный для того, чтобы использовать симайликовский Error без проблем.

anonymous
()
Ответ на: комментарий от anonymous

C++ ошибок не прощает.

Именно поэтому в C++ добавляют средства для того, чтобы воспрепятствовать появлению ошибок. Игнорировать их — это не есть признак большого ума.

У меня есть опыт достаточный для того, чтобы использовать симайликовский Error без проблем.

Судя по тому, что вы пишете, у вас достаточно опыта, чтобы думать, что вы бы использовали бы смайликовский Error без проблем. Но недостаточно, чтобы понять, что это заблуждение. И вы ошибетесь рано или поздно. А уж ваши коллеги ошибутся еще быстрее.

eao197 ★★★★★
()
Ответ на: комментарий от eao197

Игнорировать их — это не есть признак большого ума.

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

Но недостаточно, чтобы понять, что это заблуждение. И вы ошибетесь рано или поздно. А уж ваши коллеги ошибутся еще быстрее.

Это голословно. Не надо мне рассказывать обо мне и моих коллегах. Я тут типа аноним :-)

anonymous
()
Ответ на: комментарий от anonymous

Так и тут.

Да, да, вот это, по-вашему мнению, нормальный вариант:

Error parse(...) {
  ...
  if(somethig_wrong)
    return Error(fmt::format("something is going wrong: {}", msg).c_str());
  ...
}

Ну okay.

Виноват-то все равно C++ окажется.

Это голословно.

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

eao197 ★★★★★
()
Ответ на: комментарий от eao197

Да, да, вот это, по-вашему мнению, нормальный вариант:

Это вариант «прострела ноги». Нормальный вариант:

Error parse(...) {
  ...
  if(somethig_wrong)
    return Error("something wrong");
  ...
}

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

Теперь твой счётчик «подобных утверждений» и деятелей, которые делают «подобные утверждения», увеличился ещё на 1.

anonymous
()
Ответ на: комментарий от anonymous

Это вариант «прострела ноги». Нормальный вариант:

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

То, что вы здесь вдвоем защищаете, вообще не нуждается в работе со строками и/или указателями на строки. Можно обойтись обычным enum-ом:

enum class parsing_result {
  ok,
  something_wrong,
  ...
};
const char * strresult(parsing_result r) {
  switch(r) {
    case parsing_result::ok: return "no error";
    case parsing_result::something_wrong: return "something wrong";
    ...
  }
  return "unknown error";
}
...
parsing_result parse(...) {
  ...
  if(something_wrong()) return parsing_result::something_wrong;
  ...
}
И никаких проблем ни с динамическими строками, ни с временными буферами.

Ну и да, strresult можно пометить как noexcept, если компилятор поддерживает noexcept.

eao197 ★★★★★
()
Ответ на: комментарий от eao197

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

Вот именно, я использую C++. Использую в своих целях, использую так, как считаю нужным. Ну прям как авторы picojson.

То, что вы здесь вдвоем защищаете, вообще не нуждается в работе со строками и/или указателями на строки. Можно обойтись обычным enum-ом:

Да, можно. Но тогда для каждой ошибки придётся пополнять enum, пополнять strresult(), т.е., как говорят там у них, это «error prone». А защищаю я здесь типовую безопасность, как я уже сказал.

И никаких проблем ни с динамическими строками, ни с временными буферами.

Да, действительно.

Ну и да, strresult можно пометить как noexcept, если компилятор поддерживает noexcept.

Ну наконец-то.

anonymous
()
Ответ на: комментарий от anonymous

А защищаю я здесь типовую безопасность, как я уже сказал.

Сдается мне, ваши понятия о типобезопасности и способах ее достижения находятся очень далеко от моих.

eao197 ★★★★★
()

Исключения медленные.

Исключения это исключения. Когда необходимо обработать исключительную ситуацию, собственно, плевать на производительность. Если ты навешиваешь логику на исключения (например, для выхода из вложенных циклов) то ты ССЗБ. А за совет не использовать исключения в принципе, я бы тебя лично инквизировал.

dynamic_cast медленные

(виртуальное)наследование само по себе медленное. По крайней мере, медленнее чем структуры^Wблоки байт языка С. И че?

comp00 ★★★★
()
Последнее исправление: comp00 (всего исправлений: 1)
Ответ на: комментарий от anonymous

Но тогда для каждой ошибки придётся пополнять enum, пополнять strresult(), т.е., как говорят там у них, это «error prone».

Кстати говоря, при работе с enum class современные компиляторы C++ становятся все умнее и умнее: https://godbolt.org/g/4OB1mo

eao197 ★★★★★
()
Ответ на: комментарий от eao197

Кстати говоря, при работе с enum class современные компиляторы C++ становятся все умнее и умнее: https://godbolt.org/g/4OB1mo

А Страуструп об этом писал ещё в своей TC++PL 3rd в контексте обычных enum. Было это в 1998, 18 лет назад.

anonymous
()
Ответ на: комментарий от eao197
switch(r) {
    case parsing_result::ok:

ещё бы перестали требовать имя перечисления в такой ситуации

next_time ★★★★★
()
Ответ на: комментарий от tailgunner

у NT нет и официального API микроядра, доступного извне
Официальный API - Win32.

Win32 - не API ведра, а API win32-подсистемы (существовавшей в NT 4 наряду с POSIX и OS/2, и дропнутных на 2K).

Официальный API ведра - kernel32.dll.

LamerOk ★★★★★
()
Ответ на: комментарий от tailgunner

ядро NT - такой же монолит, как Linux. Но, в отличие от Linux, они засунули в ядро и оконную систему

ЕМНИП, окна в gdi32.dll, который де-юре не ведро.

LamerOk ★★★★★
()
Ответ на: комментарий от anonymous

Потому что в C++ очень просто создавать свои типы данных для типовой безопасности.

Ну не так уж очень.

DarkEld3r ★★★★★
()
Ответ на: комментарий от eao197

noexcept нужно ставить тогда, когда принципиально важно, что методы не генерируют исключений. Например, если это move-operator или функция swap. В остальных случаях все не так однозначно.

И какие минусы могут быть от «лишних» noexcept? Случай когда они расставлены ошибочно, естественно, не интересен.

DarkEld3r ★★★★★
()
Ответ на: комментарий от anonymous

Но string вообще ни к месту.

С этим не спорю. Как по мне, лучше тогда применять optional и variant/Result, раз уж мы не хотим использовать исключения, а всё остальное костыли примерно одного порядка.

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

DarkEld3r ★★★★★
()
Ответ на: комментарий от anonymous

Но тогда для каждой ошибки придётся пополнять enum, пополнять strresult(), т.е., как говорят там у них, это «error prone»

Это ещё почему? Современные компиляторы умеют выдавать предупреждения, если какой-то кейс пропущен, так что после добавления значение в enum не получится забыть добавить описание.

Правда возвращать при этом «unknown error» не вижу смысла - если уж очень хочется, то такое значение стоит добавить и в enum.

DarkEld3r ★★★★★
()
Ответ на: комментарий от DarkEld3r

Расставляя noexcept я фиксирую контракт своего класса. Например, написав однажды:

class parsing_result {
public :
  const std::string & error_desc() const noexcept {...}
  ...
};
убрать в дальнейшем noexcept при смене деталей работы parsing_result будет не просто. Т.к. ничто не скажет пользователям моего класса parsing_error, что noexcept исчез.

eao197 ★★★★★
()
Ответ на: комментарий от DarkEld3r

Это ещё почему? Современные компиляторы умеют выдавать предупреждения, если какой-то кейс пропущен, так что после добавления значение в enum не получится забыть добавить описание.

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

anonymous
()
Ответ на: комментарий от eao197

убрать в дальнейшем noexcept при смене деталей работы parsing_result будет не просто.

Ты написал изначально не так:

const std::string & error_desc() const noexcept {...}
а так:
const std::string & error_desc() const noexcept { return desc_; }
И ты ссылку на данные возвращаешь, не может быть там исключения. Конечно, если не рвать себе на голове волосы, предполагая какое-нибудь кэширование в mutable-член данных в const-методе, о котором любят писать эксперты в своих книжках.

anonymous
()
Ответ на: комментарий от anonymous

Кого-то смущают накладные расходы на этапе выполнения, меня же смущают накладные расходы на сопровождение, в первую очередь.

Я правильно понимаю - возврат чистого Error, проверку которого можно пропустить и компилятор ничего не скажет, уменьшает накладные расходы на сопровождение?

tailgunner ★★★★★
()
Ответ на: комментарий от eao197

Т.к. ничто не скажет пользователям моего класса parsing_error, что noexcept исчез.

Да, аргумент.

DarkEld3r ★★★★★
()
Ответ на: комментарий от tailgunner

Я правильно понимаю - возврат чистого Error, проверку которого можно пропустить и компилятор ничего не скажет, уменьшает накладные расходы на сопровождение?

Нет, ты не правильно понимаешь. (Только не бань меня больше.) Правильно эмитировать исключение Error.

anonymous
()
Ответ на: комментарий от anonymous

Ты написал изначально не так:

Ну посмотрите, как было написано изначально.

И ты ссылку на данные возвращаешь, не может быть там исключения.

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

eao197 ★★★★★
()
Ответ на: комментарий от anonymous

В любом случае, это дополнительная работа.

Конкретно эта работа с очень большой вероятностью «окупается»: все описания в одном месте находиться будут, а не размазаны по коду - исключается (уменьшается) вероятность добавить «почти такое же описание».

Опять же, по вариантам енума клиент логику строить может и если мы изменяем/добавляем что-то, то оно на этапе компиляции отвалится. А вот изменение строк пройдёт незамеченным, да и вообще с ними работать неудобно, если требуется что-то ещё кроме как вывести ошибку.

DarkEld3r ★★★★★
()
Ответ на: комментарий от eao197

Ну посмотрите, как было написано изначально.

Вот как было написано изначально: https://www.linux.org.ru/forum/development/12649936?cid=12661944 (комментарий)

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

Как видно из первоначального варианта, error_desc() объявлена встроенной. В публичном интерфейсе засветилась и сама реализация. Так что там поневоле появляются знания о работе кода (из одной инструкции).

anonymous
()
Ответ на: комментарий от DarkEld3r

Конкретно эта работа с очень большой вероятностью «окупается»: все описания в одном месте находиться будут, а не размазаны по коду - исключается (уменьшается) вероятность добавить «почти такое же описание».

Иногда централизация - это хорошо, иногда - плохо.

А вот изменение строк пройдёт незамеченным, да и вообще с ними работать неудобно, если требуется что-то ещё кроме как вывести ошибку.

Для этого лучше структурированный Error, а не enum.

anonymous
()
Ответ на: комментарий от anonymous

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

Подожди! Но ведь весь C++ именно об этом!!11

LamerOk ★★★★★
()
Ответ на: комментарий от anonymous

В публичном интерфейсе засветилась и сама реализация.

Специально для дебилов: реализация, пусть даже inline-функций, не может рассматриваться в качестве интерфейса или деталей интерфейса.

eao197 ★★★★★
()
Ответ на: комментарий от anonymous

Для этого лучше структурированный Error, а не enum.

Как показало обсуждение, написать нормальный структурированный Error анонимным аналитикам не под силу.

eao197 ★★★★★
()
Ответ на: комментарий от anonymous

Для этого лучше структурированный Error, а не enum.

Одно другому не мешает. Внутри этого «структурированного Error» вполне может быть значение из enum и метод для получения текста ошибки.

DarkEld3r ★★★★★
()
Ответ на: комментарий от LamerOk

Подожди! Но ведь весь C++ именно об этом!!11

Ну, как бы, да :-)

anonymous
()
Ответ на: комментарий от comp00

(виртуальное)наследование само по себе медленное

Любопытно: и часто тебе виртуальное наследование встречается? Чур только не пенять на надоевший пример со стримами.

DarkEld3r ★★★★★
()
Вы не можете добавлять комментарии в эту тему. Тема перемещена в архив.