Why should default parameters be added last in C++ functions?
Asked Answered
O

9

25

Why should default parameters be added last in C++ functions?

Oza answered 14/7, 2009 at 6:18 Comment(1)
It's not useless. Understanding language design (and in particular the design of the language(s) you use) should IMO be encouraged. To do this, among other things you need to find out why your ideas for "obvious, simple" improvements are impractical, if they are impractical. And why the designers of that language missed an opportunity, if they are not impractical. I'm pretty sure language design is related to programming.Mcnalley
M
35

To simplify the language definition and keep code readable.

void foo(int x = 2, int y);

To call that and take advantage of the default value, you'd need syntax like this:

foo(, 3);

Which was probably felt to be too weird. Another alternative is specifying names in the argument list:

foo(y : 3);

A new symbol would have to be used because this already means something:

foo(y = 3); // assign 3 to y and then pass y to foo.

The naming approach was considered and rejected by the ISO committee because they were uncomfortable with introducing a new significance to parameter names outside of the function definition.

If you're interested in more C++ design rationales, read The Design and Evolution of C++ by Stroustrup.

Mumbletypeg answered 14/7, 2009 at 6:27 Comment(6)
Why can't we use foo(3) instead of foo(,3)? Is there any problem that prevents the compiler to understand the programmer's intension? I think a better reason would be to reduce the chance of ambiguity during compile time and make the construction of compilers easier.Teeters
In the D & E, Stroustrup says named-parameter idiom is tricky in C++ due to separation of function-declaration and function-definition. If the user uses one name in the declaration and another in the implementation the compiler will have to neglect one.Brooklyn
Abhay - I don't think he says that - I think he reports that some people on the committee thought that and gives their reasons. For all we know, Stroustrup himself would have been fine with making a dependency on the names in the declaration and ignoring the definition (I would have).Mumbletypeg
@askalee - the problem is for people reading the code, not the compiler. Without named parameters, all we have to go on is position. You can always define another function with a different name if you want a version that accepts a single parameter with another meaning.Mumbletypeg
@asklee, what if both your pars have a default, and you pass in foo(3)?Rotl
@BorisCallens Then obviously 3 is bound to y. Just like it is right now, only back to front.Loy
P
24

If you define the following function:

void foo( int a, int b = 0, int c );

How would you call the function and supply a value for a and c, but leave b as the default?

foo( 10, ??, 5 );

Unlike some other languages (eg, Python), function arguments in C/C++ can not be qualified by name, like the following:

foo( a = 10, c = 5 );

If that were possible, then the default arguments could be anywhere in the list.

Pharynx answered 14/7, 2009 at 6:23 Comment(12)
what about cases which like below..foo(int a,double b=0.0,char c='a')Oza
in fact, if the compiler is smart enough, we should be able to write foo(10, 5). The compiler should interpret it as foo(10, default, 5).Teeters
@raj: That is allowed in C++. You can think of it as something like, once you have started giving default arguments you have to give all the way to the end (i.e. until the last parameter) without leaving anything in between.Pullover
@askalee: It will not be simple. Imagine a case where I have specified two default arguments in the middle, and I want to override one of the default arguments. Then how should the compiler guess which one to override.Pullover
@raj - it's nice you're asking questions because it lets us get repuation from it, but on the other hand, have you considered trying these things out in a C++ compiler? You'd very easily find the answers immediately that way.Mumbletypeg
@raj: Hmm..yes, it will 'work' in the sense that the code will compile. However the result is not as expected. Param a will receive the value 5 and param b (not c) will receive the ascii value of 'c' which is 99. This is because of the implicit conversion from char to double done by the compiler.Pullover
@Naveen: Overriding parameters should not be an issue here, because that already happens in cases that defaults are added last. For cases that the compiler can guess, they should be compiled. For cases that the compiler can't guess, just raise errors.Teeters
@Earwicker I dont this this question can be answered by compiler.And if you are mentioning about reply to naveen qn there I am trying to clarify my qn hereOza
@raj, @boris: It's a pity that some of the "solutions" you propose don't exist. I think we have just too few unexpected implicit casts to look for in C++.Dybbuk
@askalee: The compiler guess! We have enough problems with unexpected automatic conversions between types. Adding this would open a whole new can of worms in maintainability problems.Primal
As a side note, C# 4.0 will have this :)Rotl
VB allows default parameters in the middle and you can just call foo(10, , 5) or foo 10,, 5. But it's ugly and less readableFilipino
H
6

Imagine you had a function with this prototype:

void testFunction(bool a = false, bool b = true, bool c);

Now suppose I called the function like this:

testFunction(true, false);

How is the compiler supposed to figure out which parameters I meant to supply values for?

Halsy answered 14/7, 2009 at 6:24 Comment(1)
It would figure it out because there would be rules for that. For example, the arguments would bind to the second and third parameters.Loy
C
4

As most of the answers point out, having default parameters potentially anywhere in the parameter list increases the complexity and ambiguity of function calls (for both the compiler and probably more importantly for the users of the function).

One nice thing about C++ is that there's often a way to do what you want (even if it's not always a good idea). If you want to have default arguments for various parameter positions, you can almost certainly do this by writing overloads that simply turn around and call the fully-parameterized function inline:

 int foo( int x, int y);
 int foo( int y) {
     return foo( 0, y);
 }

And there you have the equivalent of:

 int foo( int x = 0, int y);
Cominform answered 14/7, 2009 at 7:26 Comment(0)
I
1

As a general rule function parameters are processed by the compiler and placed on the stack in right to left order. Therefore it makes sense that any parameters with default values should be evaluated first.

(This applieds to __cdecl, which tends to be the default for VC++ and __stdcall function declarations).

Importunacy answered 14/7, 2009 at 6:27 Comment(2)
It's not specified at all by the language standard, so it can't be the reason for how the language works - these are two completely separate levels of concern.Mumbletypeg
I'll admit that I didn't realise that, but I know that it's the bahaviour of VC++ and gcc compilers.Importunacy
L
1

Its because it uses the relative position of arguments to find to which parameters they correspond.

It could have used the types to identify that an optional parameter was not given. But implicit conversion could interfere with it. Another problem would be programming errors that could be interpreted as optional arguments drop out instead of missing argument error.

In order to allow any argument to become optional, there should be a way to identify the arguments to make sure there is no programming error or to remove ambiguities. This is possible in some languages, but not in C++.

Lynelllynelle answered 14/7, 2009 at 6:29 Comment(0)
P
1

Another thing that the standards committee had to consider was how default parameters interacts with other features, like overloaded functions, template resolution, and name lookup. These features interact in very complex and hard to describe ways already. Making default parameters be able to appear anywhere would only increase the complexity.

Panda answered 14/7, 2009 at 16:43 Comment(0)
K
1

It is a matter about call convention. Call Convention: When you call a function, the parameters are pushed in stack from right to left. e.g.

fun(int a, int b, int c);

the stack is like this: a b c so, if you set the default value from left to right like this:

fun(int a = 1, int b = 2, int c);

and call like this:

fun(4,5);

your call means set a = 4, b = 5, and c = no value; // which is wrong!

if you declare the function like this:

fun(int a, int b = 2, int c = 3);

and call like this: fun(4, 5);

your call means set a = 4, b = 5, and c = default value(3); // which is right!

In conclusion, you should put the default value from right to left.

Khat answered 25/8, 2015 at 3:47 Comment(0)
W
0

Jing Zeng is correct. I would like to add my remarks here. When a function is called, the arguments are pushed onto the stack from right to left. For example, let's say you have this arbitrary function.

int add(int a, int b) {
  int c;
  c = a + b;
  return c;
}

Here is the stack frame for the function:

------
  b
------
  a
------
 ret
------
  c
------

This diagram above is the stack frame for this function! As you can see, first b is pushed onto the stack, then a is pushed onto the stack. After that the function return address is pushed onto the stack. The function return address holds the location in main() from where the function was originally called, and after the function is done executing, the execution of the program goes to that function's return address. Then any local variables such as c are pushed onto the stack.

Now the key thing is that arguments are pushed onto the stack from right to left. Basically any default parameters that are supplied are literal values, which are stored in the code section of an executable. When the program execution encounters a default parameter without a corresponding argument, it pushes that literal value onto the top of the stack. Then it looks at a and pushes the argument's value onto the top of the stack. The stack pointer always points to the top of the stack, your most recently pushed variable. So any literal values you pushed onto the stack as default parameters are "behind" the stack pointer.

It was probably more efficient for the compiler to quickly first push arbitrary default literal values onto the stack first, since they're not stored in a memory location, and build up the stack quickly. Think about what would have been if the variables would have been pushed onto the stack first, and then the literals. Accessing a memory location for the CPU takes up a relatively long time compared to pulling a literal value out of a circuit or CPU register. Since it takes more time to push variables onto the stack vs. literals, the literals would have to wait, then the return address would have to wait, and the local variables would have to wait as well. It's probably not a big concern in efficiency, but that's just my theory for why default arguments are always in the rightmost positions of a function header in C++. It means that the compiler was designed as such.

Wardship answered 12/12, 2017 at 18:54 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.