Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

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?

like image 444
farmer Avatar asked May 30 '18 00:05

farmer


Video Answer


1 Answers

C++ has two different mechanisms in place to write a piece of code that behaves differently based on the types of the objects acted on:

  • virtual functions and inheritance, which work at runtime, and
  • template functions, which work at compile-time.

Your example with auto parameters (which apparently weren't actually adopted in C++14 except for lambda functions) works with templates. The code you've written is equivalent to

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

This code assumes that the type of T can be determined at compile-time, since the compiler needs to know the type of T in order to fill in the template. Once the compiler knows this it can say "Ah, I know what that type is! I'll generate code to directly call the name function in that type, and I know exactly what function that will call."

On the other hand, virtual functions and inheritance work at runtime. For example, suppose you want to write a function that reads some data from the network, then hands back either a Circle or a Rectangle. You might have some code like this:

Shape* myShape = decodeNetworkData();

Here, all the compiler knows is that myShape points to some sort of Shape, but it can't tell whether that's a circle or a square. Therefore, if you were to call

cout << myShape->name();

then the compiler would say "I know you're calling some version of name, but I don't know which one. But that's okay! I'll generate some code that looks at the dynamic type of myShape (the type of the thing it actually points at) and uses that to look up which function to call."

Notice that the code the compiler will generate in each case is different and the behavior will be different. In the first case, the compiler knows exactly which function to call. In the second, the compiler doesn't know what function to call, and has to generate some extra code to make things work. But, on the other hand, if you didn't have a Shape type with a virtual name function, you could make the "decode the bytes of the network" code snippet work with your first function, since the compiler would have to know, in advance, what type it was going to see come in over the network.

There was a proposal to mark this question as a duplicate of this older question on templates and inheritance, even though it's not superficially the same question. Once you know that the auto keyword in this context means "this is really a template function," you can look over that other proposed question to get some additional examples of the difference between static polymorphism (with templates) and runtime polymorphism (with virtual functions).

like image 182
templatetypedef Avatar answered Sep 30 '22 03:09

templatetypedef