虚拟调度如何与协变返回类型交互



通过C++对协变返回类型的支持,您可以编写以下内容:

class Base {};
class Derived : public Base {};
...
class A {
public:
A(Base* b) : b_(b) {}
virtual Base* Method() { return b_; }
private:
Base* b_;
}
class B : public A {
public:
B(Derived* b) : A(b) {}
// Return `Derived*` type so callers can access functionality specific to
// `Derived` (i.e., not in `Base`).
Derived* Method() override {
static_assert(std::is_base_of_v<Base, Derived>);
// Need to call `A::Method()` since `b_` is private.
// Alternatively, we can make `b_` protected and access it directly.
return dynamic_cast<Derived*>(A::Method());
}
}

似乎在大多数使用协变返回类型的情况下,派生类的方法(上面的B::Method()(只会进行动态强制转换并返回较窄的指针类型。它不会做任何其他事情。

考虑到这一假设,是否通常不Method()使用虚拟调度(即删除virtualoverridden关键字(来提高性能更好毕竟,任何在A*类型上操作的泛型代码都必须使用Base*类型来存储A::Method()的返回值(即使指针指向的对象的实际类型是Derived(,但只要Base正确使用虚拟调度,那么无论是Base的方法还是Derived的方法,都会在Base*上调用正确的方法。

在大多数使用协变返回类型的情况下,派生类的方法(上面的B::method(((只会进行动态强制转换并返回较窄的指针类型。它不会做任何其他事情。

我对此不太确定。

这两个功能有两个不同的用途:

  • virtual方法允许根据调用程序的运行时类型在运行时选择要执行的代码
  • 返回类型协方差利用类型系统来进一步约束返回类型;最小API";满足CCD_ 14给出的条件。这是编译时检查

我强烈认为B::Method根本不应该以其当前的形式存在,让我们采用以下层次结构:

struct Base{
virtual ~Base()=default;
};
struct D1:Base{};
struct D2:Base{};

struct A {
virtual Base* Method() { return new D1{}; }
};
struct B1 : A {
D1* Method() override {
return dynamic_cast<D1*>(A::Method());
}
};
struct B2 : A {
D2* Method() override {
return dynamic_cast<D2*>(A::Method());
}
};

这种用法:


#include <cassert>
int main()
{   
B1 b1;
B2 b2;
A* a = &b1;
assert(a->Method()!=nullptr);
a = &b2;
assert(a->Method()==nullptr);
}

这似乎有点奇怪,你会如何编写这个A::Method的文档?它可以用来做什么?也许是过滤器?

我认为更常见的例子,至少从我所看到的来看,是派生方法自己产生派生结果。以RequestResponsevirtual Response* Request::process方法为例。特殊请求可能会得到特殊回复。

为了回答您的问题:如果删除virtual,您将失去虚拟调度,如果用户代码可以使用静态多态性,那么这是肯定的。不过,根据我的经验,如果在某些Foo中开始用T替换Base*类型的成员变量,那么处理这些现在异构的Foo<T>的存储所需的模板化代码往往会传播得相当快。为了什么,只为了表演?当然,您之所以考虑这一点,只是因为您测量了代码,并且它是应用程序中的一个明显瓶颈。

最新更新