Default argument
In computer programming, a default argument is an argument to a function that a programmer is not required to specify. In most programming languages, functions may take one or more arguments. Usually, each argument must be specified in full (this is the case in the C programming language[1]). Later languages (for example, in C++) allow the programmer to specify default arguments that always have a value, even if one is not specified when calling the function.
Default arguments in C++
Consider the following function declaration:
int my_func(int a, int b, int c=12);
This function takes three arguments, of which the last one has a default of twelve. The programmer may call this function in two ways:
result = my_func(1, 2, 3);
result = my_func(1, 2);
In the first case the value for the argument called c is specified as normal. In the second case, the argument is omitted, and the default value of 12 will be used instead.
There is no means to know if the argument has been specified by the caller or if the default value was used.
The above-mentioned method is especially useful when one wants to set default criteria so that the function can be called with or without parameters. Consider the following:
void printToScreen(istream &input = cin)
{
// this outputs any input to the screen
cout << input;
}
The function call:
printToScreen();
will by default print input from the keyboard to the screen. As this is the most commonly used behaviour it makes sense not to specify
printToScreen(cin);
on the other hand any parameter of type istream can now be passed to the same function and the function will print to screen from the source specified as the parameter to the function. Consider:
printToScreen(fileName);
where fileName is a file that has been opened to read via ifstream's open function call.
Default argumets' values in virtual methods are taken from type of the pointer and implementation is taken from the type of the object. In this example the output will be 1 Derived
:
class Base
{
public: virtual void foo(int x=1) {
std::cout << x << " Base";
}
};
class Derived : public Base
{
public: void foo(int x=2) override {
std::cout << x << " Derived";
}
};
int main () {
Base* x = new Derived;
x->foo(); // "1 Derived"
return 0;
}
Default arguments for member functions may be specified both in declaration and definition. But in order to call such member function with substituted values the definition must be visible before a first call. Example :
struct C {
void foo(int i, int j = 99);
C(int a);
};
C::C(int = 5) { }
void C::foo(int a = 88, int b) { }
Overloaded methods
Some other languages, like Java, do not have default arguments. However, the same behaviour can be simulated by using method overloading to create overloaded methods of the same name, which take different numbers of arguments; and the versions with fewer arguments simply call the versions with more arguments, with the default arguments as the missing arguments:
int MyFunc(int a, int b) { return MyFunc(a, b, 12); }
int MyFunc(int a, int b, int c) { /* main implementation here */ }
However in addition to several other disadvantages, since the default arguments are not modeled in the type system, the type of a callback (aka higher-order function) can’t express that it accepts either of the overloads nor simulate the default arguments with overloaded functions. Whereas, in JavaScript the non-overloaded function definition can substitute the default when the input value is undefined
(regardless if it was implicitly undefined
via the argument’s absence at the call site or an explicitly passed undefined
value); which is modeled as an optional argument parameter type ?:
in TypeScript. JavaScript’s solution is not resolved statically (i.e. not at compile-time, which is why TypeScript models only the optionality and not the default values in the function’s type signature) thus incurs additional runtime overhead, although it does provide more flexibility in that callbacks can independently control their defaults instead of centrally dictated by the (callback’s type signature in the) type signature of the function which inputs the callback. The TypeScript solution can be simulated in Java with the Optional
type except the analogous of an implicit undefined
for each absent argument is an explicit Optional.<Integer>absent()
at the call site.
Evaluation
For every function call default argument values must be passed to the called function. **This leads to some code bloat - in contrast to function overloads.** [Comment: Why so? Someone who wrote or understand the previous full sentence please help explain it. Otherwise I think it is the function overloads lead to source code bloat - you would need to write (at least) one extra function signagure rather than just write one more parameter.][Comment response: the original text appears to maybe allude to languages which implement default arguments duplicated at each call site in the code generated by the language compiler or interpreter (i.e. not referring to the source code which of course overloaded methods can bloat), rather than at the definition site (the choice has tradeoffs either way), per my explanation in the Overloaded methods section.]
If a default argument is not simply a literal, but an expression, there is a choice of when the expression is evaluated – once for the entire program (at parse time, compile time, or execution time), or once per function call, at call time.
Python is a notable language that evaluates expressions in default arguments once, at module load time. If evaluation per function call is desired, it can be replicated by having the default argument be a sentinel value, such as None
in Python, and then in the first line of the function, checking for the sentinel value and assigning to expression if found, which ensures expression evaluation.
For example, to have a default argument evaluated to the current time in Python, the following may be used:
import datetime
def f(a, b=None):
b = b or datetime.datetime.now()
Extent
Generally a default argument will behave identically to an argument passed by parameter or a local variable declared at the start of the function, and have the same scope and extent (lifetime) as a parameter or other local variable, namely an automatic variable which is deallocated on function termination.
In other cases a default argument may instead be statically allocated. If the variable is mutable, it will then retain its value across function calls, as with a static variable.
This behavior is found in Python for mutable types (such as lists). As with evaluation, in order to ensure the same extent as a local variable, one can use a sentinel value, as in this example:
def f(a, b=None):
b = b or []