Question

Is overriding a pure virtual function with default arguments good or bad?

class Base {
public:
    virtual int func(int i, int j = 10) = 0;
};

class Derived : public Base {
public:
    int func(int i, int j = 20) override;
};
Was it helpful?

Solution

I would not venture to do this. At best, as @Bart van Ingen Schenau said, it will cause you nasty surprises. Furthermore, I would not even recommend using default parameters in virtual functions in the first place. If you want to have something like that, I suggest doing something like this:

class Base {
public:
    virtual int func(int i, int j) = 0;
    virtual int func(int i)
    {
        func(i, 10);
    }
};

class Derived : public Base {
public:
    int override func(int i, int j)
    {
        //Some override code
    }

    int override func(int i)
    {
        func(i, 20);
    }
};

You provide the basic funcionality with the most specific method, and provide easier methods to developers using your classes so they do not have to worry about default values for parameters. That way, you have bigger flexibility for intervention and you always know which method was called and how.

OTHER TIPS

In principle, there's nothing wrong with overriding a function with default-arguments.

In practice, there's a huge chance that the override will contain different defaults, which is a huge source of confusion and consternation. Especially if those changed defaults lead to different behavior, the code now not only depending on the object but also knowledge at the call-site.

So, avoid overriding functions with defaults, and if you really must do so, be sure to provide the exact same defaults where the overridden function had defaults, though you can add additional ones.
Just having a non-overridable functions with defaults forwarding to an overridable protected function is an easy and rarely less efficient way to guard against getting that wrong.
(It's less efficient if the forwarder cannot be inlined for any reason.)


A different way to look at the issue is to see a function with defaults not as a single function, but as a whole bunch of overloads, only one of which is overridable and can have its address taken.

Having some of those overloads missing or doing something different would be a violation of the Liskov Substitution Principle for the static type (important for templates and code you write directly), just as having the overridden function violating the base-class-contract would be for both the static and dynamic type.

EDIT

It IS a terrible idea! Although you will call the overridden method, it will get the base class defaults

c# code

public class Base
{
    public virtual int Func(int i = 10)
    {
        Console.WriteLine("base");
        return i;
    }
}
public class Derived : Base
{
    public override int Func(int i = 20)
    {
        Console.WriteLine("derived");
        return i;
    }
}
Base b = new Derived();
Derived d = new Derived();
Console.WriteLine(b.Func());
Console.WriteLine(d.Func());

//ouput
derived
10
derived
20

c++ code

    #include <iostream>
#include <string>

class Base {
public:
    virtual int func(int i = 10)
    {
        std::cout << "base";
        return i;
    };
};

class Derived : public Base {
public:
    int  func(int i = 20) override
    {
        std::cout << "derived";
        return i;
    };
};

int main()
{
    Base* b;
    Derived d;
    b = &d;
    std::cout << b->func();  
    std::cout << d.func(); 
}
//output 
derived10derived20 

Crazy!

It’s a very very bad idea and here’s why:

When you have any instance of an object (especially one that you refer to by using a reference or pointer), you have a declared class (if you declare X* p then *p is declared to be an instance of X), and it’s actual type (if Y is a subclass if X and you assign p = new Y() then *p is actually an instance of Y.

If you call a non-virtual function that is overridden, the compiler will call the function according to the declared type. That’s often wrong. With a virtual function, the compiler will call the correct implementation (the one of Y, and everything works fine).

With your changed default value, you get a mixture of both which is the worst thing possible: The compiler calls the function belonging to the actual class, but passes the default parameters according to the declared class.

Whether you declare p as an X* or an Y* shouldn’t make a difference, but suddenly it does - a different default argument is used.

Make the argument non-default and add another virtual function without that argument, which calls the origin function passing what should have been the default argument, then override as needed.

Licensed under: CC-BY-SA with attribution
scroll top