c++ final是否意味着所有方面的final ?

时间:2021-12-07 18:22:14

C++11 added final.

c++ 11最后补充说。

Finally!

终于!

I understand final does two things:

我理解final做了两件事:

  • Makes a class non-inheritable.
  • 使一个类non-inheritable。
  • Makes (virtual) functions in a class non-overridable (in a derived class).
  • 使类中的(虚)函数不可重写(在派生类中)。

Both of these seem independent of each other. But take for example the following:

这两者似乎是相互独立的。但举个例子:

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's bar.
    }
};

From above, I believe Baz being final, I should NOT need to specify that its virtual member function bar is also final. Since Baz cannot be inherited, the question of overriding bar goes out of scope. However my compiler VC++ 2015, is very quiet about this. I have not tested this on any others at the moment.

从上面来看,我认为Baz是final的,我不需要指定它的virtual member function bar也是final的。由于Baz不能继承,所以重写bar的问题超出了范围。但是我的编译器vc++ 2015,对此非常沉默。目前,我还没有在其他任何人身上做过测试。

I would be glad if someone could shed some light on this topic. A quote from the standard (if any) would be extremely appreciated. Also please state any corner cases that I am unaware of, that may cause my logical belief to fail.

如果有人能对这个话题有所了解,我会很高兴。如果有来自标准的报价(如果有的话)将非常感谢。也请说明任何我不知道的情况,可能导致我的逻辑信念失败。

So, my question is: Does a final class implicitly imply its virtual functions to be final as well? Should it? Please clarify.

所以,我的问题是:final类是否暗示它的虚函数也是final的?应该吗?请澄清。


The reason I am asking this is because final functions become qualified for de-virtualization, which is a great optimization. Any help is appreciated.

我问这个问题的原因是因为最终函数可以进行反虚拟化,这是一个很好的优化。任何帮助都是感激。

2 个解决方案

#1


8  

The reason I am asking this is because final functions become qualified for de-virtualization, which is a great optimization.

我问这个问题的原因是因为最终函数可以进行反虚拟化,这是一个很好的优化。

Do they? "De-virtualization" is not part of the C++ standard. Or at least, not really.

他们吗?“去虚拟化”不是c++标准的一部分。或者至少,不是真的。

De-virtualization is merely a consequence of the "as if" rule, which states that the implementation can do whatever it likes so long as the implementation behaves "as if" it is doing what the standard says.

去虚拟化仅仅是“假设”规则的结果,该规则规定,只要实现的行为“就像”它在做标准所说的那样,那么实现就可以做它喜欢做的任何事情。

If the compiler can detect at compile-time that a particular call to a virtual member function, through a polymorphic type, will undeniably call a specific version of that function, then it is allowed to avoid using the virtual dispatching logic and calling the function statically. That's behaving "as if" it had used the virtual dispatching logic, since the compiler can prove that this is the function that would have been called.

如果编译器能够在编译时检测到通过多态性类型对虚拟成员函数的特定调用将不可否认地调用该函数的特定版本,那么它就可以避免使用虚拟调度逻辑并静态地调用该函数。它的行为“好像”它使用了虚拟调度逻辑,因为编译器可以证明这是应该调用的函数。

As such, the standard does not define when de-virtualization is allowed/forbidden. A compiler, upon inlining a class that takes a pointer to a virtual type, may find that the pointer being passed is pointing to a stack variable local declared in the function that it is being inlined within. Or that the compiler can trace down a particular inline/call graph to the point of origin for a particular polymorphic pointer/reference. In those cases, the compiler can de-virtualize calls into that type. But only if it's smart enough to do so.

因此,标准没有定义何时允许/禁止反虚拟化。编译器在内联一个接受指向虚拟类型的指针的类时,可能会发现所传递的指针指向函数中声明的堆栈变量local,而该变量是内联的。或者编译器可以跟踪一个特定的内联/调用图到一个特定的多态指针/引用的原点。在这些情况下,编译器可以将调用反虚拟化到该类型中。但前提是它足够聪明地这么做。

Will a compiler devirtualize all virtual function calls to a final class, regardless of whether those methods are declared final themselves? It may. It may not. It may not even devirtualize any calls to methods declared final on the polymorphic type. That's a valid (if not particularly bright) implementation.

不管这些方法本身是否被声明为final类,编译器是否会将所有的虚函数调用都去虚拟化?它可能。它可能不是。它甚至可能不会去虚拟化对在多态性类型上声明为final的方法的任何调用。这是一个有效的实现(如果不是特别聪明的话)。

The question you're asking is implementation specific. It can vary from compiler to compiler.

您要问的问题是实现的特殊性。它可以由编译器到编译器不同。

However, a class being declared final, as you pointed out, ought to be sufficient information for the compiler to devirtualize all calls to pointers/references to the final class type. If a compiler doesn't do so, then that's a quality-of-implementation issue, not a standards one.

但是,正如您所指出的,一个被声明为final的类应该是编译器的足够信息,它可以将所有对最终类类型的指针/引用的调用进行反虚拟化。如果编译器不这样做,那么这是实现的质量问题,而不是标准问题。

#2


4  

To quote the draft C++ standard from here [class.virtual/4]:

从这里引用c++标准草案[class.virtual/4]:

If a virtual function f in some class B is marked with the virt-specifier final and in a class D derived from B a function D::f overrides B::f, the program is ill-formed.

如果某个类B中的虚函数f被标记为病毒说明符final,而在类D中则由B a函数D:::f覆盖B: f,则程序是病态的。

And here [class/3]:

这里(类/ 3):

If a class is marked with the class-virt-specifier final and it appears as a base-type-specifier in a base-clause (Clause [class.derived]), the program is ill-formed.

如果一个类被标记为class-virt-specifier final,并且它在base-clause(子句[class.派生])中作为一个基本类型的说明符出现,那么这个程序就不正确了。

So, in answer to the question;

回答这个问题;

Does a final class implicitly imply its virtual functions to be final as well? Should it? Please clarify.

一个final类是否暗示它的虚函数也是final的?应该吗?请澄清。

So, at least not formally. Any attempt to violate either rule will have the same result in both cases; the program is ill-formed and won't compile. A final class means the class cannot be derived from, so as a consequence of this, its virtual methods cannot be overridden.

所以,至少不是正式的。任何违反任何一条规则的企图都将在这两种情况下产生相同的结果;该程序格式不良,无法编译。最后一个类表示不能派生类,因此,它的虚拟方法不能被重写。

Should it? At least formally, probably not; they are related but they are not the same thing. There is also no need formally require the one to imply the other, the effect follows naturally. Any violations have the same result, a failed compilation (hopefully with appropriate error messages to distinguish the two).

应该吗?至少在形式上,可能不是;它们是相关的,但它们不是同一件事。也没有必要正式地要求其中一个暗示另一个,效果自然就会产生。任何违反都有相同的结果,即编译失败(希望使用适当的错误消息来区分两者)。


To touch on your motivation for the query and the de-virtualization of the virtual calls. This is not always immediately affected by the final of the class nor method (albeit they offer help), the normal rules of the virtual functions and class hierarchy apply.

讨论查询的动机和虚拟调用的去虚拟化。这并不总是直接影响到类的final和方法(尽管它们提供了帮助),但是虚拟函数和类层次结构的正常规则是适用的。

If the compiler can determine that at runtime a particular method will always be called (e.g. with an automatic object, i.e. "on the stack"), it could apply such an optimisation anyway, irrespective of the method is final or not. These optimisations fall under the as-if rule, that allow the compiler to apply any transformation so long as the observable behaviour is as-if the original code had been executed.

如果编译器可以在运行时确定某个特定的方法将始终被调用(例如,使用一个自动对象,即:“在堆栈上”),它无论如何都可以应用这样的优化,不管方法是否最终。这些optimisations属于as-if规则,允许编译器应用任何转换,只要可以观察到的行为是原始代码已经执行。

#1


8  

The reason I am asking this is because final functions become qualified for de-virtualization, which is a great optimization.

我问这个问题的原因是因为最终函数可以进行反虚拟化,这是一个很好的优化。

Do they? "De-virtualization" is not part of the C++ standard. Or at least, not really.

他们吗?“去虚拟化”不是c++标准的一部分。或者至少,不是真的。

De-virtualization is merely a consequence of the "as if" rule, which states that the implementation can do whatever it likes so long as the implementation behaves "as if" it is doing what the standard says.

去虚拟化仅仅是“假设”规则的结果,该规则规定,只要实现的行为“就像”它在做标准所说的那样,那么实现就可以做它喜欢做的任何事情。

If the compiler can detect at compile-time that a particular call to a virtual member function, through a polymorphic type, will undeniably call a specific version of that function, then it is allowed to avoid using the virtual dispatching logic and calling the function statically. That's behaving "as if" it had used the virtual dispatching logic, since the compiler can prove that this is the function that would have been called.

如果编译器能够在编译时检测到通过多态性类型对虚拟成员函数的特定调用将不可否认地调用该函数的特定版本,那么它就可以避免使用虚拟调度逻辑并静态地调用该函数。它的行为“好像”它使用了虚拟调度逻辑,因为编译器可以证明这是应该调用的函数。

As such, the standard does not define when de-virtualization is allowed/forbidden. A compiler, upon inlining a class that takes a pointer to a virtual type, may find that the pointer being passed is pointing to a stack variable local declared in the function that it is being inlined within. Or that the compiler can trace down a particular inline/call graph to the point of origin for a particular polymorphic pointer/reference. In those cases, the compiler can de-virtualize calls into that type. But only if it's smart enough to do so.

因此,标准没有定义何时允许/禁止反虚拟化。编译器在内联一个接受指向虚拟类型的指针的类时,可能会发现所传递的指针指向函数中声明的堆栈变量local,而该变量是内联的。或者编译器可以跟踪一个特定的内联/调用图到一个特定的多态指针/引用的原点。在这些情况下,编译器可以将调用反虚拟化到该类型中。但前提是它足够聪明地这么做。

Will a compiler devirtualize all virtual function calls to a final class, regardless of whether those methods are declared final themselves? It may. It may not. It may not even devirtualize any calls to methods declared final on the polymorphic type. That's a valid (if not particularly bright) implementation.

不管这些方法本身是否被声明为final类,编译器是否会将所有的虚函数调用都去虚拟化?它可能。它可能不是。它甚至可能不会去虚拟化对在多态性类型上声明为final的方法的任何调用。这是一个有效的实现(如果不是特别聪明的话)。

The question you're asking is implementation specific. It can vary from compiler to compiler.

您要问的问题是实现的特殊性。它可以由编译器到编译器不同。

However, a class being declared final, as you pointed out, ought to be sufficient information for the compiler to devirtualize all calls to pointers/references to the final class type. If a compiler doesn't do so, then that's a quality-of-implementation issue, not a standards one.

但是,正如您所指出的,一个被声明为final的类应该是编译器的足够信息,它可以将所有对最终类类型的指针/引用的调用进行反虚拟化。如果编译器不这样做,那么这是实现的质量问题,而不是标准问题。

#2


4  

To quote the draft C++ standard from here [class.virtual/4]:

从这里引用c++标准草案[class.virtual/4]:

If a virtual function f in some class B is marked with the virt-specifier final and in a class D derived from B a function D::f overrides B::f, the program is ill-formed.

如果某个类B中的虚函数f被标记为病毒说明符final,而在类D中则由B a函数D:::f覆盖B: f,则程序是病态的。

And here [class/3]:

这里(类/ 3):

If a class is marked with the class-virt-specifier final and it appears as a base-type-specifier in a base-clause (Clause [class.derived]), the program is ill-formed.

如果一个类被标记为class-virt-specifier final,并且它在base-clause(子句[class.派生])中作为一个基本类型的说明符出现,那么这个程序就不正确了。

So, in answer to the question;

回答这个问题;

Does a final class implicitly imply its virtual functions to be final as well? Should it? Please clarify.

一个final类是否暗示它的虚函数也是final的?应该吗?请澄清。

So, at least not formally. Any attempt to violate either rule will have the same result in both cases; the program is ill-formed and won't compile. A final class means the class cannot be derived from, so as a consequence of this, its virtual methods cannot be overridden.

所以,至少不是正式的。任何违反任何一条规则的企图都将在这两种情况下产生相同的结果;该程序格式不良,无法编译。最后一个类表示不能派生类,因此,它的虚拟方法不能被重写。

Should it? At least formally, probably not; they are related but they are not the same thing. There is also no need formally require the one to imply the other, the effect follows naturally. Any violations have the same result, a failed compilation (hopefully with appropriate error messages to distinguish the two).

应该吗?至少在形式上,可能不是;它们是相关的,但它们不是同一件事。也没有必要正式地要求其中一个暗示另一个,效果自然就会产生。任何违反都有相同的结果,即编译失败(希望使用适当的错误消息来区分两者)。


To touch on your motivation for the query and the de-virtualization of the virtual calls. This is not always immediately affected by the final of the class nor method (albeit they offer help), the normal rules of the virtual functions and class hierarchy apply.

讨论查询的动机和虚拟调用的去虚拟化。这并不总是直接影响到类的final和方法(尽管它们提供了帮助),但是虚拟函数和类层次结构的正常规则是适用的。

If the compiler can determine that at runtime a particular method will always be called (e.g. with an automatic object, i.e. "on the stack"), it could apply such an optimisation anyway, irrespective of the method is final or not. These optimisations fall under the as-if rule, that allow the compiler to apply any transformation so long as the observable behaviour is as-if the original code had been executed.

如果编译器可以在运行时确定某个特定的方法将始终被调用(例如,使用一个自动对象,即:“在堆栈上”),它无论如何都可以应用这样的优化,不管方法是否最终。这些optimisations属于as-if规则,允许编译器应用任何转换,只要可以观察到的行为是原始代码已经执行。