How to use the ellipsis in C++

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.


Ellipsis code example

#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 parameters
va_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 argument
for (int i = 0; i < num; i++) {
mul *= va_arg(list, int);
}
// Ends the use of va_list
va_end(list);
// Returns the product
return 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 output
cout << "Product is " << mul;
return 0;
}

Ellipsis code explanation

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.

Downsides to the use of ellipsis

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 value
int mul = product(5, 1.3, 2, 3, 4, 5);
// Printing output
cout << "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

Copyright ©2025 Educative, Inc. All rights reserved