Подтвердить что ты не робот

Что говорит стандарт С++ о потере спецификатора throw в деструкторе по умолчанию

Три разных компилятора показывают три разных поведения, компилирующих этот код:

class MyException : public std::exception
{
 public:
  MyException(std::string str) : m_str(str) {}
  virtual const char * what() const throw () {return m_str.c_str(); }
 protected:
  std::string m_str;
};

Sun С++ 5.8 Патч 121017-22 2010/09/29: Предупреждение Функция MyException:: ~ MyException() может вызывать только исключения, исключаемые функцией std:: exception:: ~ exception ( ) он переопределяет

g++ 3.4.3: Ошибка спецификатор looser throw для `virtual MyException:: ~ MyException() '

Visual Studio 2005: Очень рад (ни ошибка, ни предупреждение)

class exception {
public:
  exception () throw();
  exception (const exception&) throw();
  exception& operator= (const exception&) throw();
  virtual ~exception() throw();
  virtual const char* what() const throw();
}

Я знаю, в чем проблема и как я могу это исправить:

class MyException : public std::exception
{
 public:
  MyException(std::string str) : m_str(str) {}
  virtual const char * what() const throw () {return m_str.c_str(); }
  ~MyException() throw() {}  <------------ now it is fine!
 protected:
  std::string m_str;
};

Однако мне интересно, что говорит стандарт в конкретной ситуации.

Я провел еще один небольшой тест в Visual Studio 2005, и нашел что-то, что меня действительно удивляет:

struct Base
{
    virtual int foo() const throw() { return 5; }
};

struct Derived : public Base
{
    int foo() const { return 6; }
};

int main()
{
    Base* b = new Derived;
    std::cout << b->foo() << std::endl; //<-- this line print 6!!!
    delete b;
}

Подпись двух функций различна. Как это может работать? Кажется, что visual studio 2005 полностью игнорирует спецификацию исключения.

struct Base
{
    virtual int foo() const throw() { return 5; }
};

struct Derived : public Base
{
    int foo() { return 6; } // I have removed the const keyword 
                            // and the signature has changed
};

int main()
{
    Base* b = new Derived;
    std::cout << b->foo() << std::endl; // <-- this line print 5
    delete b;
}

Является ли это стандартом С++? Есть ли какой-либо волшебный флаг для установки?

Что относительно VS2008 и VS2010?

4b9b3361

Ответ 1

Ваша программа плохо сформирована в соответствии со стандартом С++ и, следовательно, демонстрирует поведение, которое невозможно объяснить в рамках стандарта С++.

Справка:
Стандарт С++ 03:

15.4 Спецификации исключений [except.spec]

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

[Пример:

 struct B 
 {
    virtual void f() throw (int, double);
    virtual void g();
 };
 struct D: B 
 {
    void f(); // ill-formed
    void g() throw (int); // OK
 };

Объявление D::f плохо сформировано, поскольку оно допускает все исключения, тогда как B::f допускает только int и double. ]

Ответ 2

Он немного развился в С++ 11 [except.spec]:

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

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

Однако этот случай сложный, потому что деструктор фактически синтезируется самим компилятором!

В С++ 03, я думаю, что Стандарт не был настолько осторожен в этом, и вам пришлось писать их самостоятельно, в С++ 11, однако мы получаем:

14/ Неявно объявленная специальная функция-член (раздел 12) должна иметь спецификацию исключения. Если f является неявным объявленным конструктором по умолчанию, конструктором копирования, конструктором перемещения, деструктором, оператором присваивания копии или оператором присваивания перемещения, его неявная спецификация исключений указывает тип-идентификатор T if и only если T разрешено спецификацией исключений для функции, непосредственно вызываемой неявным определением f s; f разрешает все исключения, если какая-либо функция, которую он вызывает напрямую, разрешает все исключения, а f не допускает исключений, если каждая вызываемая им функция не позволяет исключений.

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

Примечание. VS2005 является одним из наименее стандартизованных компиляторов, которые вы можете найти на Земле.