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

Конечный ли С++ подразумевает окончательный во всех аспектах?

С++ 11 добавлен окончательный.

Наконец-то!

Я понимаю, что final выполняет две вещи:

  • Делает класс ненаследуемым.
  • Делает (виртуальные) функции в классе, не переопределяемом (в производном классе).

Оба они кажутся независимыми друг от друга. Но возьмите, например, следующее:

class Foo
{
    public:
    virtual void bar()
    {
        //do something unimportant.
    }
};
class Baz final : public Foo
{
    public:
    void bar() /*final*/ override
    {
        //do something more important than Foo bar.
    }
};

Сверху я считаю Baz final, я должен НЕ указать, что его функция virtual bar также final. Поскольку Baz нельзя унаследовать, вопрос об переопределении bar выходит за рамки. Однако мой компилятор VС++ 2015, очень тихий об этом. Я не тестировал это на других в данный момент.

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

Итак, мой вопрос: Имеет ли final class неявно подразумеваемые функции virtual final также? Должно ли это? Просьба уточнить.


Причина, по которой я спрашиваю об этом, состоит в том, что функции final становятся квалифицированными для де виртуализации, что является большой оптимизацией. Любая помощь приветствуется.

4b9b3361

Ответ 1

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

Они? "Де-виртуализация" не является частью стандарта C++. Или, по крайней мере, не совсем.

Де-виртуализация является лишь следствием правила "как будто", которое гласит, что реализация может делать все, что ей нравится, при условии, что реализация ведет себя "как если бы" она делает то, что говорит стандарт.

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

Таким образом, стандарт не определяет, когда де-виртуализация разрешена/запрещена. Компилятор, вставив функцию, которая получает указатель на тип базового класса, может обнаружить, что передаваемый указатель указывает на переменную стека local, объявленную в функции, в которую он встроен. Или что компилятор может отследить определенный встроенный граф/граф вызовов до точки происхождения для определенного полиморфного указателя/ссылки. В этих случаях компилятор может де-виртуализировать вызовы в этот тип. Но только если он достаточно умен, чтобы сделать это.

Будет ли компилятор девиртуализировать все вызовы виртуальных функций в final классе, независимо от того, были ли эти методы объявлены как final сами? Это может. Может и нет. Он даже не может девиртуализировать любые вызовы методов, объявленных final для полиморфного типа. Это правильная (если не особенно яркая) реализация.

Вопрос, который вы задаете, зависит от конкретной реализации. Это может варьироваться от компилятора к компилятору.

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

Ответ 2

Чтобы процитировать проект стандарта С++ здесь [class.virtual/4]:

Если виртуальная функция f в некотором классе B отмечена спецификатором virt final и в классе D, полученном из B, функция D::f переопределяет B::f, программа плохо сформирован.

И здесь [класс/3]:

Если класс помечен спецификатором class-virt-t final и он выглядит как спецификатор базового типа в базовом-предложение (Clause [class.derived]), программа плохо сформирована.

Итак, в ответ на вопрос;

Неявно ли подразумевается, что final class подразумевает, что его функции virtual равны final? Должно ли это? Просьба уточнить.

Итак, по крайней мере, формально. Любая попытка нарушить любое правило будет иметь одинаковый результат в обоих случаях; программа плохо сформирована и не будет компилироваться. A final class означает, что класс не может быть получен, поэтому в результате его методы virtual не могут быть переопределены.

Должно ли это? По крайней мере, формально, вероятно, нет; они связаны, но они не одно и то же. Также нет необходимости формально требовать, чтобы одна подразумевала другую, эффект следует естественным образом. Любые нарушения имеют одинаковый результат, неудачную компиляцию (надеюсь, с соответствующими сообщениями об ошибках, чтобы отличить их).


Чтобы затронуть вашу мотивацию для запроса и де-виртуализацию виртуальных вызовов. Это не всегда сразу влияет на final класса и метода (хотя они предлагают помощь), применяются обычные правила виртуальных функций и иерархии классов.

Если компилятор может определить, что во время выполнения всегда будет вызван конкретный метод (например, с автоматическим объектом, то есть "в стеке" ), он может применять такую ​​оптимизацию в любом случае, независимо от метода, является окончательным или нет. Эти оптимизация подпадает под правило as-if, которое позволяет компилятору применять любое преобразование, пока наблюдаемое поведение будет таким, как если бы исходный код был выполнен.