A function in C++ can accept a different number of arguments so long as their types are explicitly specified beforehand. However, if we are uncertain of the number of arguments we need to pass in a function, we can use ellipsis. Also known as a variable argument list, the ellipsis (...
) allows us to pass a varying number of arguments of varying types in a function.
The compiler neither checks the types of parameters nor the number of parameters that the function should accept with ellipses. This allows the user to pass a variable number of arguments of any type.
Functions using ellipsis are written as:
return_type functionName(parameter_list, ...)
Where parameter_list
is one or more specified parameters, and ...
(preceded by a comma) is any number of additional parameters.
Note: At least one non-ellipsis parameter must be specified (even if it is not used) when declaring functions that use ellipsis. The specified last non-ellipsis parameter acts as the location threshold/placeholder and helps access the ellipsis parameters.
#include <cstdarg>double product(int num, ...) //num is the location threshold{//An object list of type va_list is declared and is used to iterate on ellipsis parametersva_list list;//va_list object is initialized to access the//values after last non-ellipsis specified parameter 'num'va_start(list, num);int mul = 1;// Iterate through every argumentfor (int i = 0; i < num; i++) {mul *= va_arg(list, int);}// Ends the use of va_listva_end(list);// Returns the productreturn mul;}int main(){// Calling the function// The first number '5' is the list is the non-ellipsis parameter we specified//and will not be multiplied.int mul = product(5, 1, 2, 3, 4, 5);// Printing outputcout << "Product is " << mul;return 0;}
In line 1, we first include the cstdarg
header. The specified parameter and the macros (from the cstdarg
header) help access the ellipsis parameters. These macros are: va_arg
, va_copy
, va_start
, and va_end
.
Next, in line 4, we declare our function that uses the ellipsis. The function declares an object (list
) of type va_list
, a special type used to access the values in the ellipsis. (Conceptually, we can think of va_list
as a pointer pointing to the array of values in ellipsis).
Next in line 7, we call va_start()
macro to initialize the position of va_list
, which then points to the first ellipsis parameter. It takes two parameters: the va_list
and the last specified non-ellipsis parameter.
To get each ellipsis parameter, the va_arg
macro requires the va_list
object (list
) and the type of the last non-ellipsis parameter. Our code example contains only one specified parameter, num
of type int.
Each call to va_arg()
in line 16 moves the va_list
to the next ellipsis parameter.
Lastly, in line 20, we call va_end()
with the list
parameter to end the variable argument processing.
Even though the use of ellipsis offers a lot of flexibility, there are some downsides to it. Since all argument types are allowed with ellipsis, the compiler completely forgoes type checking and fails to throw an error if we pass a different parameter type than what is expected.
int main(){// using type double instead of integer, still compiles the code but gives a garbage valueint mul = product(5, 1.3, 2, 3, 4, 5);// Printing outputcout << "Product is " << mul;return 0;}
The above example shows how using values of different types does not throw an error and instead gives us the garbage value.
Though we can develop different solutions like using a sentinel value, a decoder string, and/or passing a length parameter to overcome the downsides of using ellipsis, it would be better to avoid using it.
Free Resources