Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

default parameter in virtual functions C++

I read about the inheritance mechanism in C++ and about virtual functions.

according to my knowlendge (in all examples I have encountered), inherited methods had the same signature as the parent class'.

My question is the following: I know that function default parameter value is not a part of function signature.

Can I define this value to be some constant in the parent Class virtual function, and in the derived class declare and implement the overriding method without this default value.

In this case, when I call the derived object's method using a pointer to parent class, will the function be called with/without this default initializion?

thanks

like image 953
Day_Dreamer Avatar asked Dec 20 '22 07:12

Day_Dreamer


1 Answers

Default arguments are mostly syntactic sugar and get determined at compile time. Virtual dispatch, on the other hand, is a run-time feature. It would probably be least surprising to have that default parameter chosen that was defined alongside with the function that actually gets called but this is not possible (at least not without additional run-time overhead) for the reason stated above.

Therefore, the default parameter is selected by the compiler using the static type of the object a member function is called upon. Let's see an example.

#include <iostream>
#include <memory>

class Base
{

public:

  virtual void
  f(int a, int b = 1)
  {
    std::cout << "Base: a = " << a << ", b = " << b << "\n";
  }
};

class Derived : public Base
{

public:

  virtual void
  f(int a = 1, int b = 2) override
  {
    std::cout << "Derived: a = " << a << ", b = " << b << "\n";
  }
};

int
main()
{
  std::unique_ptr<Base> base_as_base {new Base {}};
  std::unique_ptr<Base> derived_as_base {new Derived {}};
  std::unique_ptr<Derived> derived_as_derived {new Derived {}};
  base_as_base->f(0);        // Base:    a = 0, b = 1
  derived_as_base->f(0);     // Derived: a = 0, b = 1
  // derived_as_base->f();   // compiler error
  derived_as_derived->f(0);  // Derived: a = 0, b = 2
  derived_as_derived->f();   // Derived: a = 1, b = 2
}

I agree that this is confusing. Please don't write code like this. Fortunately, there is a simple workaround. Apart from not using default parameters at all, we can use an idiom called non-virtual interfaces. The virtual function is made protected and not given any default parameters. It is then only called indirectly by a non-virtual function from the base class. That function can have all default parameters defined in a single place.

#include <iostream>
#include <memory>

class Base
{

public:

  void
  f(int a, int b = 1)
  {
    this->impl(a, b);
  }

protected:

  virtual void
  impl(int a, int b)
  {
    std::cout << "Base: a = " << a << ", b = " << b << "\n";
  }
};

class Derived : public Base
{

protected:

  virtual void
  impl(int a, int b) override
  {
    std::cout << "Derived: a = " << a << ", b = " << b << "\n";
  }
};

int
main()
{
  std::unique_ptr<Base> base_as_base {new Base {}};
  std::unique_ptr<Base> derived_as_base {new Derived {}};
  std::unique_ptr<Derived> derived_as_derived {new Derived {}};
  base_as_base->f(0);        // Base:    a = 0, b = 1
  derived_as_base->f(0);     // Derived: a = 0, b = 1
  derived_as_derived->f(0);  // Derived: a = 0, b = 1
}
like image 77
5gon12eder Avatar answered Jan 06 '23 22:01

5gon12eder