Компилятор знает что-то, чего вы не знаете. [[noreturn]]
не является частью сигнатуры метода, поэтому переопределенные методы в производных классах могут свободно возвращаться.
// 'function' must return a value
#pragma warning(default:4716)
class Base
{
public:
[[noreturn]] virtual void ErrorHandler()
{
throw 0;
}
int f(int x)
{
if (x > 0)
return x;
else ErrorHandler(); // C4716
}
};
class Derived :
public Base
{
public:
[[noreturn]] void ErrorHandler() final // FINAL is key
{
throw 1;
}
int f(int x)
{
if (x > 0)
return x;
else ErrorHandler(); // No C4716 here!
}
};
class Derived2 :
public Base
{
public:
void ErrorHandler() override
{
return; // AHA! ErrorHandler returns!
}
};
int main()
{
Base b;
b.f(0);
Derived d;
d.f(0);
Derived2 d2;
d2.f(0);
}
здесь я создал дополнительный производный класс и добавил метод f
в Derived
.
Единственное предупреждение, которое я получаю:
<source>(17) : warning C4715: 'Base::f': not all control paths return a value
, правильно выводит, что Derived::f
(копия Base::f
) не имеет этой проблемы .
Проблема в Base::f
обозначена Derived2
- в ней ErrorHandler
была переопределена без атрибута [[noreturn]]
. Атрибуты не являются частью сигнатур методов .
Так что virtual ErrorHandler
, то есть [[noreturn]]
, может быть переопределено тем, который возвращает. Это приведет к тому, что f
будет демонстрировать неопределенное поведение.
Когда в Derived
я отмечаю ErrorHandler
как final
, f
там не может демонстрировать неопределенное поведение ( поскольку нет способа переопределить ErrorHandler
с перегрузкой не [[noreturn]]
). Таким образом, предупреждение не генерируется.
Вы все еще можете подавить это предупреждение, если предположите, что никто не собирается переопределять ErrorHandler
на возвращаемое.
Самый чистый способ this:
class Base
{
public:
[[noreturn]] void DoErrorHandling() {
ErrorHandler();
throw 0; // or std::terminate
}
private:
[[noreturn]] virtual void ErrorHandler() { throw 0; }
public:
int f(int x) {
if (x > 0)
return x;
DoErrorHandling();
}
};
Теперь мы обертываем ErrorHandler
не виртуальным DoErrorHandling
методом, помеченным [[noreturn]]
, который, если виртуальный ErrorHandler
, который он вызывает, не выбрасывает, выбрасывает.