Use of variadic function in C++CXX-W2014
C++ allows functions to accept a variable number of arguments, which are known as variadic functions. These functions can have more formal arguments during invocation than what is specified in function declaration. Variadic functions can be defined using two mechanisms in C++, namely function parameter packs and the use of a C-style ellipsis as the final parameter declaration.
Variadic functions are flexible because they can accept a varying number of arguments of different types. However, using a C-style ellipsis to define a variadic function can be hazardous as it provides no mechanisms to check the type safety(see section Recommended below) of the arguments being passed or ensure that the number of arguments matches the function definition. This could result in undefined behavior when calling the function at runtime, which may be exploited to run arbitrary code.
It is recommended not to define C-style variadic functions and instead of it use it along with function parameter packs as shown below.
Bad practice
#include <cstdarg>
int sum(int p1, int p2, ...) {
int result = p1 + p2;
va_list rest;
va_start(rest, p2);
// Loop terminates only if a 0 is passed as one of the argument. If zero is
// missing, then standards declare it as undefined behaviour.
while (int v = va_arg(rest, int)) {
result += v;
}
va_end(va);
return result;
}
Recommended
// A minimul example for parameter unpacking
template<typename... Ts>
int sum(Ts... args) {
// we can get the size at compile time
constexpr int size = sizeof...(args);
const int items[size] = {args...}; // a static array
int sum = 0;
for (int i=0; i<size; ++i)
sum += items[i];
return sum;
}
// Note: This examaple only demonstrate the use of parameter unpacking but is
// not safe against types. The next example shows a type safe alternative
or
#include <type_traits>
// There are two things happening here
//
// 1. Compiler will define the necessary template specialisation based on the
// arguments passed during invocation. Hence, this code donot rely on user input
// to terminate processing user input.
//
// 2. Using std::enable_if, compiler can perform argument type checking, like
// function sum only allow int type values.
//
template <typename OprType, typename std::enable_if<std::is_integral<OprType>::value>::type * = nullptr>
int sum(OprType f, OprType s) { return f + s; }
template <typename OprType, typename... Ts, typename std::enable_if<std::is_integral<OprType>::value>::type * = nullptr>
int sum(OprType f, Ts... rest) {
return f + sum(rest...);
}