首页 > 解决方案 > Can virtual functions be replaced with auto parameters?

问题描述

This question follows up on stackoverflow.com/q/2391679

One of the classic examples of virtual functions is

class Shape
{
public:
    virtual string draw() = 0;
};

class Circle : public Shape
{
public:
    string draw() { return "Round"; }
};

class Rectangle : public Shape
{
public:
    string draw() { return "Flat"; }
};

void print (Shape& obj)
{
    cout << obj.draw();
}

However, we can instead pass an auto parameter in C++ 14

class Circle
{
public:
    string draw() { return "Round"; }
};

class Rectangle
{
public:
    string draw() { return "Flat"; }
};

void print (auto& shape)
{
    cout << shape.draw();
}

When should we prefer virtual functions or auto parameters?

Is the latter more efficient due to early binding?

标签: templatespolymorphismc++14virtual-functionsauto

解决方案


C++ 有两种不同的机制来编写一段代码,这些代码的行为根据所作用的对象的类型而有所不同:

  • virtual在运行时工作的函数和继承,以及
  • 模板函数,在编译时工作。

您的带有auto参数的示例(除了 lambda 函数,显然在 C++14 中实际上并未采用)适用于模板。你写的代码相当于

template <typename T>
void print(T& shape) {
    cout << shape.name();
}

此代码假定T可以在编译时确定类型,因为编译器需要知道类型T才能填写模板。一旦编译器知道这一点,它就可以说“啊,我知道那个类型是什么!我将生成代码以直接调用该name类型的函数,并且我确切地知道将调用什么函数。”

另一方面,虚函数和继承在运行时工作。例如,假设您要编写一个从网络读取一些数据的函数,然后返回 aCircle或 a Rectangle。你可能有一些这样的代码:

Shape* myShape = decodeNetworkData();

在这里,编译器只知道myShape指向某种Shape,但它无法判断那是圆形还是方形。因此,如果你打电话

cout << myShape->name();

然后编译器会说“我知道你在调用某个版本name,但我不知道是哪个版本。但这没关系!我将生成一些查看动态类型的代码myShape(它实际上是指向)并使用它来查找要调用的函数。”

请注意,编译器在每种情况下生成的代码是不同的,行为也会不同。在第一种情况下,编译器确切地知道要调用哪个函数。其次,编译器不知道要调用什么函数,并且必须生成一些额外的代码才能使事情正常进行。但是,另一方面,如果您没有具有Shapename函数的类型,则可以使“解码网络字节”代码片段与您的第一个函数一起使用,因为编译器必须提前知道,它将通过网络看到什么类型。

有人提议将此问题标记为有关模板和继承的旧问题的重复,即使它表面上不是同一个问题。一旦您知道auto此上下文中的关键字意味着“这确实是一个模板函数”,您就可以查看其他提出的问题,以获得静态多态性(使用模板)和运行时多态性(使用虚函数)之间差异的一些额外示例.


推荐阅读