【问题标题】:How can I form a `final` reference to a non-final class?如何形成对非最终类的“最终”引用?
【发布时间】:2020-05-12 21:43:16
【问题描述】:

final 是一个很好的关键字。它让我可以防止从一个类继承。它还允许编译器在调用虚函数或访问虚基时跳过运行时调度机制。

现在假设我有一些非finalT 带有虚函数和/或基类。还假设我有一个对此类的实例的引用。 我如何告诉编译器这个特定的引用是对完全派生的完整对象,而不是对派生更多类型的基本子对象?

我的动机是像 optionalvector 这样的课程。如果我调用optional<T>::operator*(),我会得到一个T&。但是,我可以肯定地知道这个引用确实是一个T,而不是一些更派生的对象。这同样适用于vector<T> 以及我访问其元素的所有方式。

我认为在这种情况下跳过动态分派会是一个很好的优化,尤其是在调试模式和不够聪明的编译器上,无法查看 optionalvector 实现并去虚拟化调用。

【问题讨论】:

  • 像这样的“伟大的优化”通常会带来不那么高的代价,即降低代码的可读性并且在执行时间上没有实际差异。
  • 这里只是吹毛求疵,但final(以及override)并不是真正的关键字。它们是仅在某些上下文中识别的特殊标识符。拥有一个名为final 的函数是完全有效的。如果它是一个成员函数,你甚至可以将它标记为 final 就像 void final() final; :)
  • 你甚至可以设计一个final final() final; 有效的场景。
  • 正确的做法是标记你的Tfinal。你为什么不这样做?您可以确定 T& 确实是对编译器强制执行的具体 T 的引用。当有人将您的T 子类化并且您的“确定性”消失时,以任何其他方式进行操作只是在自找麻烦。更不用说编译器在识别引用的具体类型和跳过虚拟调度方面可能比你想象的更聪明......
  • 您是在谈论单个翻译单元中的所有代码吗?或者该函数的实现是否在与可选翻译单元不同的翻译单元中引用?

标签: c++ c++11 inheritance polymorphism final


【解决方案1】:

形式上,你可以这样做:

void final(A &a) {
  static_cast<A*>(dynamic_cast<void*>(&a))->foo();
}

dynamic_cast&lt;void*&gt; 返回一个指向最派生类型的指针(而static_cast 来自 void* 不能选择多态基类),因此编译器可以知道A::foo 是叫。然而,编译器似乎并没有利用这些信息。他们甚至会生成明显的额外指令来实际执行动态转换(即使如果失败当然会是未定义的行为)。

当然,只要详细程度和通用性允许,您就可以通过实际编写 a.A::foo() 来解除虚拟化。

【讨论】:

  • @NicolBolas:我不是依靠“完全定义”,而是依靠相似性规定;尽管如此,您是正确的,因为 多个、重叠(名义上的)T[1] 对象的可能性。即使使用&amp;a+2,Clang 和 GCC 也不做任何事情(如果没有真正的支持数组,这本身就是未定义的行为!)。同时,我将只使用dynamic_cast 技巧。
  • struct Ancestor : A {}; struct Derived : Ancestor, A {}; Derived d; final(d); 这一切都已完全定义,但动态/静态转换产生的 A&amp; 与传入的不同。
  • @Filipp:我认为您对优化有一定的看法,但确切的情况是模棱两可的。
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2019-09-01
  • 1970-01-01
相关资源
最近更新 更多