1

Я создал свой класс MyException

class MyException: public std::exception
{
private:
    std::string msg;
public:
    MyException(std::string msg): std::exception(msg)
    {
}

};

Пример взят из интернета, но у меня ругается компилятор (gcc) на строчку std::exception(msg)

note: candidate: 'constexpr std::exception::exception(const std::exception&)'
no matching function for call to 'std::exception::exception(std::__cxx11::string&)'
     MyException(std::string msg): std::exception(msg){
no known conversion for argument 1 from 'std::__cxx11::string' {aka 'std::__cxx11::basic_string<char>'} to 'const std::exception&'

После нашел другой пример

class MyException: public std::exception
{
private:

public: MyException(std::string msg); ~MyException() = default; const char* what() const noexcept override; };

Тут все работает, но у меня есть пару вопросов:

  1. Почему я не могу реализовать первый пример, хотя у другого человека получилось? (c++11)
  2. Почему в строчке const char(указатель) what() const noexcept override; используется последовательность const noexcept override;? Почему я не могу написать просто noexcept override или просто override. Насколько я знаю noexcept не позволяет выбросить исключение. И почему используется два раза const? Мы же уже указали, что функция будет константная
Harry
  • 221,325
Barev
  • 45
  • const (применительно к методу) используется один раз, const и noexcept с недавних пор являются частью сигнатуры. – user7860670 Apr 19 '22 at 13:11

3 Answers3

2

Дело в том, что по стандарту exception не имеет конструктора иного, кроме как по умолчанию. введите сюда описание изображения

Так что формально ваш код

MyException(std::string msg): std::exception(msg)

ошибочен. Другое дело, что некоторые компиляторы от стандарта отходят и допускают

exception::exception(const string&)

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

Harry
  • 221,325
  • То есть, это зависит от компилятора, например, от VS может допускать данный вариант, а тот же gcc нет? – Barev Apr 19 '22 at 13:35
  • Да, я на это когда-то тоже нарывался :) – Harry Apr 19 '22 at 13:36
  • Хорошо, с этим я разобрался. А почему я не могу просто использовать сигнатуру const char* what() const override; Почему обязан быть noexcept? – Barev Apr 19 '22 at 13:37
  • Ну, потому что в стандарте так :) – Harry Apr 19 '22 at 13:59
  • Понял! Спасибо! – Barev Apr 19 '22 at 14:04
  • А где можно смотреть как выглядит сигнатура этого класса? – Barev Apr 19 '22 at 14:06
  • В моем ответе :) Это из стандарта С++20. – Harry Apr 19 '22 at 17:27
  • А помимо ответа можно где-то? Если я,например, захочу посмотреть как выглядит в другом классе? :) – Barev Apr 19 '22 at 17:42
  • https://ru.stackoverflow.com/q/417797/195342 – Harry Apr 19 '22 at 18:43
1
  1. Наверное вы не внимательно смотрели, или там была опечатка, потому что конструктор std::exception не принимает в аргумент std::string, а принимает только С_строку(в некоторых реализациях). Поэтому нужно ему передать именно такую строку. Вот, например, так:

    MyException(std::string msg) 
        : std::exception(msg.c_str())...
    
  2. спецификатор const noexcept override может быть только для функции члена. Это значит, что функция член определена и для константных объектов (то есть она не изменяет состояние объекта), не генерирует исключение(помощь компилятору) и переопределен в производном классе(то есть это виртуальная функция_член).

А второй констант относится к возвращаемому типу(возвращает указатель на константную строку.

AR Hovsepyan
  • 15,934
  • с char* тоже самое error: no matching function for call to 'std::exception::exception(char&)' MyException::MyException(char msg, short code):std::exception(msg) – Barev Apr 19 '22 at 13:29
  • И почему я не могу просто указать const char* what() const override; – Barev Apr 19 '22 at 13:32
  • Нет, по стандарту у exception только один конструктор - по умолчанию. – Harry Apr 19 '22 at 13:33
  • @Harry from Kiev, по стандарту нет, но некоторые компиляторы поддерживают – AR Hovsepyan Apr 19 '22 at 16:47
  • Тогда к ответу нужно указать компилятор, для которого предназначен этот ответ... – Harry Apr 19 '22 at 17:27
1

В соседнем ответе объяснили, в чем ошибка. А теперь как чинить.

Да, можно положить в свой класс std::string с текстом ошибки, и перегрузить what(), чтобы он его возвращал. Но есть нюанс: стандартные исключения можно копировать, гарантированно не получая исключений (например от нехватки памяти) - у них внутри какой-то аналог std::shared_ptr<std::string>.

А ваше исключение при копировании сможет выбросить исключение (если new кинет исключение) - маловероятно, но все равно не по фен-шую.

Чтобы этого не произошло, можно не хранить строку самому, а унаследоваться от std::runtime_error или std::logic_error, и хранить строку в нем. Или, если совсем не хочется наследоваться, хранить один из этих классов в своем.

HolyBlackCat
  • 27,445
  • 3
  • 27
  • 40
  • Например, вот так err(const std::string& what = "") : std::runtime_error(what) {}? – Barev Apr 19 '22 at 17:48
  • Тогда мне в самом классе при наследовании надо тоже std::runtime_error указывать? или можно оставить std::exception? – Barev Apr 19 '22 at 17:49
  • @Barev Надо std::runtime_error. В конструкторе после : могут быть только родители, нельзя унаследоваться от одного класса а в конструкторе инициализировать другой. – HolyBlackCat Apr 19 '22 at 18:02