Я только что видел этот вопрос и связанные с ним ответы.
Учитывая, что я никогда раньше не сталкивался с таким способом использования throw, я был весьма удивлен, обнаружив, что это вообще возможно.
@LightnessRacesinOrbit Я понимаю вашу точку зрения, я надеюсь получить некоторое представление от кого-то, кто использует / использовал эту функцию, и проливает свет на то, почему это полезно
Обработчики исключений перехватывают исключения в зависимости от их типа. Вообще говоря, нет особых причин для искусственного ограничения того, какие типы могут быть выбраны - это может решить программист. (На самом деле есть ограничения - например, что брошенный объект можно скопировать).
Просто не было особого смысла отказываться от этого. С другого конца бросить 42; может быть намного привлекательнее, чем возврат 42; также. Однако использование исключений для управления потоком является неприятным антипаттерном, о котором все рано или поздно сожалеют.





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 сразу
Спасибо, вы дали мне именно ту информацию, которую я надеялся получить. Используя Java, я отчасти ожидал, что C++ будет иметь ту же философию «вы не должны злоупотреблять методом throw-catch», указанную в языке, хотя теперь я понимаю вашу точку зрения о том, почему в этом нет необходимости.
В 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
Я оставляю вам и другим комментаторам решать, могли ли более современные методы превзойти первоначальные идеи изобретателей.
Я считаю их очень связанными друг с другом, поэтому они вместе.