The dangers of default value with virtual functions!
Tags: cpp
Published: 2022-09-12 09:59:14

---
title: The dangers of default value with virtual functions!
published: true
description: Virtual functions and default value are not a good mix, let's discover why
tags: cpp
---

## Article::Article
Here's a snippet of code a friend send me:   
```C++
#include <iostream>
 
class Base
{
    public:
        virtual void rick(int x = 0)
        {
            if (0 == x)
                std::cout << "Give you up\n";
            else
                std::cout << "Let you down\n";
        }
};
 
class Derived : public Base
{
    public:
        virtual void rick(int x = 10)
        {
            if (0 == x)
                std::cout << "Run around and desert you\n";
            else
                std::cout << "Make you cry\n";
        }
};
 
int main()
{
    Derived d1;
    Base* bp = &d1;
 
    std::cout << "Never gonna ";
    bp->rick();
 
    return 0;
}
```
[Compiler explorer link](https://godbolt.org/z/8YxqKqGj1)
Can you deduce what you will be printed? You can see the solution in the compiler explorer link just above.
 
Personally I did find the right solution but two things helped me: first, I knew my friend send me this snippet to try to trick me, secondly, the snippet is short and I can directly smell that there is something fishy with the virtual functions, the inheritance and the default value. In a real project, it may not be that simple to spot this.

Now let's why it does act like this!

## Explanations
### A little recap
We have a base class named `Base` and a class inheriting from `Base` named `Derived`. (How original)    
There is a virtual member function `virtual void Base::rick(int x = 0)` in `Base` and a function with the same name in `Derived` : `virtual void  Derived::rick(int = 10)`    
Both member functions print a different text depending on `x` value.

In the main functions, we have a `Derived` object that we access through a `Base*` to call the `rick` member function.

It means that if we try to guess what is the output there is four possibilities:
* Never gonna Give you up
* Never gonna Let you down
* Never gonna Run around and desert you
* Never gonna Make you cry

### Step by step analysis
Let's begin by deducing which `rick` will be called, `rick` is defined in `Base` and overridden in `Derived`, and yes it works that way even if the default value of `x` is different because the default value is not part of the prototype.    
Now that we know that it is pretty easy to know which one is called: we have a `Derived` object, the function is virtual, we call it through a pointer, it will call `virtual void Derived::rick`    
We have two possibility left:
* Never gonna Run around and desert you
* Never gonna Make you cry

You may tempted to answer _"Never gonna Make you cry"_ because the default argument is 10 in `virtual void Derived::rick(int x = 10)` but that's wrong, the real output is _"Never gonna Run around and desert you"_ and the only bug here is between the chair and the keyboard :)

### Why does it do that?
Because the standard says so:
> The overriders of virtual functions do not acquire the default arguments from the base class declarations, and when the virtual function call is made, the default arguments are decided based on the static type of the object

It literally means that if you have an object of type `Derived` and you call `rick()` the default value of the argument does not depend ON which implementation is called.

## Article::~Article
As we can see, the behavior is logic, but not intuitive and can be hard to spot. That's why I think this should not be used, but if you are crazy enough to use it, there should be some comments and documentation to warn other devs about this.

## Sources
* [https://en.cppreference.com/w/cpp/language/default_arguments](https://en.cppreference.com/w/cpp/language/default_arguments)