TITLE: Default parameters can be counter-intiutive PROBLEM: When combined with overloading and inheritance, the use of default parameters can be misleading. The Crossjammer (xjam@cork.berkeley.edu) class foo { public: virtual int f(int x, int y = 0) {printf("foo: y=%d\n", y); return 0; } }; class bar : public foo { public: int f(int x, int y = 1) {printf("bar: y=%d\n", y); return 0; } }; main() { foo f; bar b; foo *fp = &b; f.f(1); b.f(1); fp->f(1); // calls foo f, should call bar f. fp->f(1, 2); // calls foo f, should call bar f. } The resulting run, when the code is compiled with g++ 1.39.1 leads to foo: y=0 bar: y=1 foo: y=0 foo: y=2 He claims that this is a bug. I claim that fp->f(1) does not match the signature of any virtual function and therefore no virtual lookup takes place. Then the compiler takes the liberty of using foo::f since it has a foo pointer. RESPONSE: Steve Clamage (steve@taumet.com) It is a bug. The correct output is foo: y=0 // f.f(1) bar: y=1 // b.f(1) bar: y=0 // fp->f(1) bar: y=2 // fp->f(1,2) First, note that the signature of bar::f(int,int) is the same as foo::f(int,int), and so the bar version is in fact a virtual function. Variable fp is declared to point to a foo, so when the expression fp->f(1) is evaluated, compiler must pick up the default value of the second parameter from foo::f(). After all, fp may point to a foo or anything derived from foo. The actual function called is determined at run time, not compile time, because the function referred to is virtual. In this case, bar::f() is called, BUT WITH THE DEFAULT PARAMETER FROM foo::f(). This illustrates one of the pitfalls of default parameters, in that you get a counter-intuitive value for the default parameter in this case. Other pitfalls involve the scope of the default expression. For example: int i; foo(int, int = i); bar(int i) { foo(1); // what is the second parameter to foo? } foo() gets called with the global 'i' as its second parameter, not the local i; this might not be what is intended. Default parameters were introduced into C++ before overloaded functions were stable and well-understood. Default parameters can always be simulated with overloaded functions, which have fewer surprises of the this kind. Example: int i; foo(int, int); inline foo(int k) { return foo(k, i); } Now you can call foo(1) with no question about which 'i' is involved, and with no more overhead than the default parameter version.