12

I have these classes:

class Base
{
    public:
        virtual void foo(int x = 0)
        {
            printf("X = %d", x);
        }
};

class Derived : public Base
{
    public:
        virtual void foo(int x = 1)
        {
            printf("X = %d", x);
        }
};

When I have:

Base* bar = new Derived();
bar->foo();

My output is "X = 0", even if foo is called from Derived, but when I have:

Derived* bar = new Derived();
bar->foo();

My output is "X = 1". Is this behavior correct? (To select default parameter value from the declaration type, instead of selecting it from actual object type). Does this break C++ polymorphism?

It can cause many problems if somebody uses virtual functions without specifying the actual function parameter and uses the function's default parameter.

Bo Persson
  • 90,663
  • 31
  • 146
  • 203
Mircea Ispas
  • 20,260
  • 32
  • 123
  • 211
  • 1
    put something in the printf() which actually tells you which function is being called – Ferruccio Mar 11 '11 at 13:00
  • It's clear that the called function is Derived::foo – Mircea Ispas Mar 11 '11 at 13:01
  • How? they both print something like "X = value". You can't use the value to determine which one is being called. Try printing "Base: X = %d" in the base and you'll be able to tell which one is called. – Ferruccio Mar 11 '11 at 13:10
  • possible duplicate of [Can virtual functions have default parameters?](http://stackoverflow.com/questions/3533589/can-virtual-functions-have-default-parameters) – Nawaz Mar 11 '11 at 13:17

3 Answers3

10

Default arguments are retained even if you override a function! And this behaviour is correct. Let me search the reference from the C++ Standard.

§8.3.6/10 [Default arguments] from the C++ Standard says,

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.

The example from the Standard itself

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()
}

Also, not only it's retained, it is evaluated everytime the function is called:

§8.3.6/9 says,

Default arguments are evaluated each time the function is called

Bill
  • 14,257
  • 4
  • 43
  • 55
Nawaz
  • 353,942
  • 115
  • 666
  • 851
  • @Nawaz: Copy/pasting from another answer instead of linking to it doesn't really help SO as a whole. Especially when someone else provided the link already. – Jon Mar 11 '11 at 13:06
  • @Jon: Copy pasting which another answer? You mean I copy pasted from another answer? Which answer? – Nawaz Mar 11 '11 at 13:09
  • @Jon: Alright. I say your answer NOW. But if you think it answers the question, then you should say it duplicate topic and vote for closing it! – Nawaz Mar 11 '11 at 13:10
  • 1
    @Nawaz: If you read the questions carefully, you will see that they are *not* the same question. Also, don't you think that retaliation downvoting is kind of immature? – Jon Mar 11 '11 at 13:12
  • @Jon: I didn't downvote. But this comment of yours atleast told me you downvoted! – Nawaz Mar 11 '11 at 13:13
  • 1
    @Nawaz: I never thought to hide the fact; I also explained my downvote. Also, isn't it surprising that the downvote I had disappeared *just now*? Ah, coincidence... Anyway, have a good day. – Jon Mar 11 '11 at 13:15
  • @Jon: Someone else might have downvoted and removed it. Anyway, your downvote and explanation doesn't make sense! – Nawaz Mar 11 '11 at 13:16
5

The behavior is correct. Check out the answer to this question for an explanation:

Can virtual functions have default parameters?

Moral: treat default parameter values as part of the function signature and do not modify them when overriding virtual functions!

Community
  • 1
  • 1
Jon
  • 428,835
  • 81
  • 738
  • 806
  • @Jon, thanks for the link, the original answer definitely gets an upvote from me... – Nim Mar 11 '11 at 13:10
  • @Jon: If you think this link answers the question, then you should vote for closing this topic, being it possible duplicate! – Nawaz Mar 11 '11 at 13:12
  • @Nawaz: as I already said: the *answer* to that question answers this one as well. The questions themselves are *not* the same. – Jon Mar 11 '11 at 13:13
  • @Jon: But you provided the link as an answer to this question. What else is he asking according to you that he cannot know reading the link? – Nawaz Mar 11 '11 at 13:14
  • I thought the moral was to avoid default parameters. They detract from the readability of the code. – Ferruccio Mar 11 '11 at 13:20
  • @Ferruccio: IMO that's a good idea, but it's kind of a personal preference or team convention thing. And you will find it rough going to persuade developers of "orthodox" C/C++ upbringing to value readability over terseness. ;) – Jon Mar 11 '11 at 13:27
  • @Jon: I think the moral is slightly misleading, as one can change the function signature slightly, and still achieving the polymorphism! – Nawaz Mar 11 '11 at 13:29
0

This is what C++ designed, I think.

The polymorphism is finished by virtual table, which internally plays with pointer to functions, but the parameter's default value is not stored with the function pointer, it is binded in compiling phase, so it can only get the default value by first looking at its type, in your first case, it is Base, then it use 0 as the default value.

Shuo
  • 8,447
  • 4
  • 30
  • 36
  • This is technically correct, but misleading as an answer because the C++ standard does not mandate that polymorphism be implemented by vtables. It specifically states what should happen in such scenarios; if it left that undefined as an implementation detail, then anyone could write a conforming compiler that used a mechanism like a vtable to bind the default values. – Jon Mar 11 '11 at 13:05
  • Yes. But for those guys working on standard, they also thinks of whether the feature is practical. I just think it designed in this way because the other way is not practical. Any functions can take many paremters all of them can have default values. That makes runtime bind a mission impossible. – Shuo Mar 11 '11 at 13:55