Почему C++ позволяет бросать что угодно?

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

  • Какая логика позволяет (почти) бросить и поймать что угодно?
  • Есть ли использование синтаксиса throw-catch, выходящего за рамки сигнализации об исключении / ошибке? И если да, то считается ли это плохой практикой или существуют «общие» способы использования этого, о которых я никогда не подозревал?

Я считаю их очень связанными друг с другом, поэтому они вместе.

GPhilo 05.12.2018 11:14

@LightnessRacesinOrbit Я понимаю вашу точку зрения, я надеюсь получить некоторое представление от кого-то, кто использует / использовал эту функцию, и проливает свет на то, почему это полезно

GPhilo 05.12.2018 11:15

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

Peter 05.12.2018 11:24

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

Hans Passant 05.12.2018 11:24
Стоит ли изучать PHP в 2026-2027 годах?
Стоит ли изучать PHP в 2026-2027 годах?
Привет всем, сегодня я хочу высказать свои соображения по поводу вопроса, который я уже много раз получал в своем сообществе: "Стоит ли изучать PHP в...
Поведение ключевого слова "this" в стрелочной функции в сравнении с нормальной функцией
Поведение ключевого слова "this" в стрелочной функции в сравнении с нормальной функцией
В JavaScript одним из самых запутанных понятий является поведение ключевого слова "this" в стрелочной и обычной функциях.
Приемы CSS-макетирования - floats и Flexbox
Приемы CSS-макетирования - floats и Flexbox
Здравствуйте, друзья-студенты! Готовы совершенствовать свои навыки веб-дизайна? Сегодня в нашем путешествии мы рассмотрим приемы CSS-верстки - в...
Тестирование функциональных ngrx-эффектов в Angular 16 с помощью Jest
В системе управления состояниями ngrx, совместимой с Angular 16, появились функциональные эффекты. Это здорово и делает код определенно легче для...
Концепция локализации и ее применение в приложениях React ⚡️
Концепция локализации и ее применение в приложениях React ⚡️
Локализация - это процесс адаптации приложения к различным языкам и культурным требованиям. Это позволяет пользователям получить опыт, соответствующий...
Пользовательский скаляр GraphQL
Пользовательский скаляр GraphQL
Листовые узлы системы типов GraphQL называются скалярами. Достигнув скалярного типа, невозможно спуститься дальше по иерархии типов. Скалярный тип...
5
4
647
5
Перейти к ответу Данный вопрос помечен как решенный

Ответы 5

1. Вы можете выбросить что угодно, потому что C++ не определяет назначение конкретных классов, как это делают java или C#. В этих языках вы можете создавать объекты только тех классов, которые наследуются от своего указанного класса исключения. В C++ нет класса исключений the one (никто не заставляет использовать std-вещи).

2. Использование throw / catch для сигнализации сообщений об ошибках называется разработкой, управляемой исключениями, и действительно считается плохой практикой. Разработка, управляемая исключениями, приводит к коду, который трудно понять. Это также может привести к тому, что разделы кода не будут выполняться непреднамеренно. Я настоятельно не рекомендую это делать.

Ответ принят как подходящий

На этот вопрос сложно ответить без предположений, но в статье Бьярна 1998 года «Обзор языка программирования C++» используются произвольные типы при описании обработки исключений и предлагается создать иерархию указанных типов для удобства / семантики. Похоже, что вначале он не имел в виду «базовый класс» exception ни для одного из них.

Возможно, идея наличия стандартной иерархии (на основе std::exception) зародилась как «дополнение», удобный способ приблизиться к предложенному Бьярном использованию исключений, а не как строительные блоки, на которых должно основываться использование исключений всеми. Эта современная практика заключается в выводе всех исключений из std::exception, которые, похоже, появились позже.

В настоящее время я не могу придумать веской причины не делать этого, хотя бы по той причине, что люди, использующие ваш код, вероятно, будут ожидать, что catch (const std::exception&) верхнего уровня поглотит что-нибудь полезное. Я также склонен вставлять catch (...) в main, на всякий случай.

Говоря более практично, если бы это было не так, потребовались бы дополнительные правила, ограничивающие throw, чтобы он был «действителен» только для выражений с типом, производным от std::exception, что, казалось бы, не имеет каких-либо реальных преимуществ, достаточных для оправдания дополнительные правила. C++, хотите верьте, хотите нет, исходит из минимализма с точки зрения вопроса «почему у нас нет правила такой-то», хотя, очевидно, его раздувание, похоже, противоречит этому после всех этих лет.

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

Exceptions are used to transfer control from a place where an error is detected to some caller that hasexpressed interest in handling that kind of errors. Clearly, this is a mechanism that should be used only for errors that cannot be handled locally. [..] Exceptions can be used to make error handling more stylized and regular.

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

Конечно, 1998 год не был «началом», но тот факт, что он все еще не говорил о std::exception, говорит мне о многом. Мне не удалось найти файл CFront PDF сразу

Lightness Races in Orbit 05.12.2018 11:32

Спасибо, вы дали мне именно ту информацию, которую я надеялся получить. Используя Java, я отчасти ожидал, что C++ будет иметь ту же философию «вы не должны злоупотреблять методом throw-catch», указанную в языке, хотя теперь я понимаю вашу точку зрения о том, почему в этом нет необходимости.

GPhilo 05.12.2018 11:40

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

Если вам не нужны (правда, небольшие) накладные расходы на создание объекта std::exception, C++ не заставляет вас платить за него. Например, вы можете напрямую выдать код ошибки, это намного проще, чем создание подкласса std::exception с элементом кода ошибки. Конечно, вызывающий ваш код должен знать, что ожидать улова чисел, а не подклассов std::exception. По этой причине, как правило, лучше использовать только подклассы std::exception, но это не обязательно.

Я не Бьярн Страуструп, но я постараюсь дать ответ.

What's the logic behind allowing (almost) anything to be thrown and caught?

Соглашение заключается в том, чтобы выбросить производную от std::exception из стандартной библиотеки. Если вы ограничиваете то, что можно бросить, вы ограничиваетесь использованием только этого. C++ пытается: 1. Дайте вам большую гибкость 2. Управляйте и без того сложной сложностью языка. Позволяя языку бросать и улавливать что угодно, вы даете программисту гибкость. Вы также делаете язык менее сложным, перемещая std::exception в стандартную библиотеку вместо того, чтобы он был частью самого языка.

Is there a use of the throw-catch syntax that goes beyond exception/error signaling?

Да, вы можете использовать его для передачи сигналов. Это опрометчивый антипаттерн (злоупотребление).

When the handler is entered, e will be a copy of the Int_overflow object that was created inside the add function to describe what went wrong. The parallel to function calls is now almost exact: The throw-expression passes an Int_overflow object to the handler declared to accept objects of class Int_overflow. The usual initialization semantics are used to pass this argument. The type of the object thrown is used to select the handler in approximately the way the arguments of a function call is used to select an overloaded function. Again, a slightly different way of looking at the throw-expression is as a return statement where the argument is not only passed back to the caller, but is also used to select which caller to return to.
[...]
Relying of such fundamental and well-supported language concepts rather than inventing some special ‘‘exception type’’ ensures that the full power of C++ is available for the definition and use of exceptions.

"Обработка исключений для C++", Эндрю Кениг, Бьярн Страуструп, 1989

Это может ответить на ваш вопрос о логике такого поведения. Создатели C++ хотели использовать уже существующие технологии для реализации новой функции.

На ваш вопрос о законном использовании этой функции я дам вам еще одну цитату из той же публикации:

We do not wish to use exception handling as a substitute for more conventional control structures

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

Другие вопросы по теме