Can Virtual Functions Have Default Parameters

Can virtual functions have default parameters?

Virtuals may have defaults. The defaults in the base class are not inherited by derived classes.

Which default is used -- ie, the base class' or a derived class' -- is determined by the static type used to make the call to the function. If you call through a base class object, pointer or reference, the default denoted in the base class is used. Conversely, if you call through a derived class object, pointer or reference the defaults denoted in the derived class are used. There is an example below the Standard quotation that demonstrates this.

Some compilers may do something different, but this is what the C++03 and C++11 Standards say:

8.3.6.10:


A virtual function call (10.3) uses
the default arguments in the
declaration of the virtual function
determined
by the static type of the pointer or reference denoting the object. An
overriding function in a derived
class does not acquire default arguments from the function it
overrides. Example:

struct A {
virtual void f(int a = 7);
};
struct B : public A {
void f(int a);
};
void m()
{
B* pb = new B;
A* pa = pb;
pa->f(); //OK, calls pa->B::f(7)
pb->f(); //error: wrong number of arguments for B::f()
}

Here is a sample program to demonstrate what defaults are picked up. I'm using structs here rather than classes simply for brevity -- class and struct are exactly the same in almost every way except default visibility.

#include <string>
#include <sstream>
#include <iostream>
#include <iomanip>

using std::stringstream;
using std::string;
using std::cout;
using std::endl;

struct Base { virtual string Speak(int n = 42); };
struct Der : public Base { string Speak(int n = 84); };

string Base::Speak(int n)
{
stringstream ss;
ss << "Base " << n;
return ss.str();
}

string Der::Speak(int n)
{
stringstream ss;
ss << "Der " << n;
return ss.str();
}

int main()
{
Base b1;
Der d1;

Base *pb1 = &b1, *pb2 = &d1;
Der *pd1 = &d1;
cout << pb1->Speak() << "\n" // Base 42
<< pb2->Speak() << "\n" // Der 42
<< pd1->Speak() << "\n" // Der 84
<< endl;
}

The output of this program (on MSVC10 and GCC 4.4) is:

Base 42
Der 42
Der 84

default parameter in virtual functions C++

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
}

Virtual function default parameters and overloading

At some point in time future maintainers of your code will be baffled, confused, and/or perplexed if you change the default values depending on which static type they call foo on so I'm going to assume that's not your concern.

Given that your concern is someone changing the default in the parent and forgetting to update the child class that's easily solved with the non-virtual interface pattern:

#include <iostream>

using namespace std;

struct Base
{
void foo(int one = 1, int two = 2) { foo_impl(one, two); }

protected:
virtual void foo_impl(int one, int two)
{ cout << "one: " << one << " two: " << two << endl; }
};

struct Derived : public Base
{
protected:
virtual void foo_impl(int one, int two)
{ Base::foo_impl(one, two); cout << " derived!" << endl; }
};

int main()
{
Base* b = new Base();
Base* d = new Derived();

Derived* dp = new Derived();

b->foo();
d->foo();
dp->foo();

return 0;
}

Virtual functions default parameters

It's a bad idea because they aren't kept anywhere.

The default values that are used will be those defined in the static (compile-time) type. So if you were to change the default parameters in an override, but you called the function through a base class pointer or reference, the default values in the base would be used.

#include <iostream>

struct Base
{
virtual ~Base(){ }
virtual void foo(int a=0) { std::cout << "base: " << a << std::endl; }
};

struct Derived : public Base
{
virtual ~Derived() { }
virtual void foo(int a=1) { std::cout << "derived: " << a << std::endl; }
};

int main()
{
Base* derived = new Derived();
derived->foo(); // prints "derived: 0"
delete derived;
}

Virtual functions and default parameters

Compiling with clang -Wall the following warnings occur:

main.cpp:14:22: warning: 'B::f4' hides overloaded virtual function [-Woverloaded-virtual]
virtual void f4(int n){
^
main.cpp:6:22: note: hidden overloaded virtual function 'A::f4' declared here: different number of parameters (0 vs 1)
virtual void f4(){
^

These warnings explain what's going on. A virtual function is only overridden by another function with the same signature.

B::f4(int) does not override A::f4() because they have different parameter lists. Instead, it is a different virtual function.

So rac.f4() just calls A::f4() which is not overridden.

Since C++11 you can help to detect this problem:

virtual void f4(int n) override {
// ^^^^^^^^

Then the compiler will give an error if this function does not actually override something from the base.

Resolution of virtual function with default parameters

Your code is actually seen by the compiler like this:

(The display() method is not actually there, but the resolving works in similar way)

class A
{
public:
virtual void display(int i) { cout<< "Base::" << i << endl; }
void display() { display(5); }
};

class B : public A
{
public:
void display(int i) override { cout<< "Derived::" << i << endl; }
void display() { display(9); }
};

Now you should understand what happens. You are calling the non-virtual display() which calls the virtual function. In more strict words: the default argument is resolved just like if the no-arg non-virtual method was there - by the type of the variable (not by the actual type of the object), but the code gets executed according to real object type, because it is virtual method:

int main()
{
A * a = new B(); // type of a is A* real type is B
a->display(); // calls A::display() which calls B::display(5)

A* aa = new A(); // type of aa is A* real type is A
aa->display(); // calls A::display() which calls A::display(5)

B* bb = new B(); // type of bb is B* real type is B
bb->display(); // calls B::display() which calls B::display(9)
}

Good practice : Default arguments for pure virtual method

I often wish to use both default parameters and virtual function as you do. The others have rightfully pointed out however that this leads to ambiguity and is generally not a good idea. There is a reasonably simple solution, one that I use. Give your virtual function a different name, make it protected, and then provide a public function with default parameters which calls it.

class Base {
protected:
virtual void vSomeMethod(const SomeStruct& t ) = 0;
public:
void someMethod( const SomeStruc& t = 0 )
{ vSomeMethod( t ); }
}

Derived classes simply override vSomeMethod and don't worry at all about the default parameters.

can a pure virtual function has parameters?

yes, a PVF can have parameters.

virtual void playCard(Player enemyPlayer) = 0;

here = 0 (is not assigning), Simply we are informing to compiler that function will be pure and does not having any body(where its declared, in that class), but it can have parameter.

From the n4659 C++ standard

A pure virtual function need be defined only if called with, or as if
with (15.4), the qualified-id syntax (8.1).

class shape {
point center;
public:
virtual void rotate(int) = 0; // pure virtual
virtual void draw() = 0; // pure virtual
};

But there is another observation

A function declaration cannot provide both a pure-specifier and a
definition — end note ]

struct C {
virtual void f() = 0 { };
};


Related Topics



Leave a reply



Submit