Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

C++11 variable number of arguments, same specific type

Question is simple, how would I implement a function taking a variable number of arguments (alike the variadic template), however where all arguments have the same type, say int.

I was thinking about something alike this;

void func(int... Arguments) 

Alternatively wont a recursive static assert on the types work?

like image 603
Skeen Avatar asked Aug 02 '13 13:08

Skeen


People also ask

What is variable number of arguments in C?

To call a function with a variable number of arguments, simply specify any number of arguments in the function call. An example is the printf function from the C run-time library. The function call must include one argument for each type name declared in the parameter list or the list of argument types.

Which function accepts a variable number of arguments?

In mathematics and in computer programming, a variadic function is a function of indefinite arity, i.e., one which accepts a variable number of arguments.

How many number of arguments functions can take?

Except for functions with variable-length argument lists, the number of arguments in a function call must be the same as the number of parameters in the function definition. This number can be zero. The maximum number of arguments (and corresponding parameters) is 253 for a single function.


2 Answers

A possible solution is to make the parameter type a container that can be initialized by a brace initializer list, such as std::initializer_list<int> or std::vector<int>. For example:

#include <iostream> #include <initializer_list>  void func(std::initializer_list<int> a_args) {     for (auto i: a_args) std::cout << i << '\n'; }  int main() {     func({4, 7});     func({4, 7, 12, 14}); } 
like image 194
hmjd Avatar answered Sep 20 '22 00:09

hmjd


Here's a version that removes the function from the overload set, instead of giving a static_assert. This is allows you to provide other overloads of the function that could be used when the types aren't all the same, rather than a fatal static_assert that can't be avoided.

#include <type_traits>  template<typename... T>   struct all_same : std::false_type { };  template<>   struct all_same<> : std::true_type { };  template<typename T>   struct all_same<T> : std::true_type { };  template<typename T, typename... Ts>   struct all_same<T, T, Ts...> : all_same<T, Ts...> { };  template<typename... T> typename std::enable_if<all_same<T...>::value, void>::type func(T...) { } 

If you want to support perfect forwarding you probably want to decay the types before checking them, so that the function will accept a mix of lvalue and rvalue arguments as long as they have the same type:

template<typename... T> typename std::enable_if<all_same<typename std::decay<T>::type...>::value, void>::type func(T&&...) { } 

Alternatively, if you have a general purpose trait for testing the logical conjunction you can do it using std::is_same instead of writing your own all_same:

template<typename T, typename... Ts> typename std::enable_if<and_<is_same<T, Ts>...>::value, void>::type func(T&&, Ts&&...) { } 

Because this requires at least one argument you'd also need another overload to support the zero-argument case:

void func() { } 

The and_ helper can be defined like so:

template<typename...>   struct and_;  template<>   struct and_<>   : public std::true_type   { };  template<typename B1>   struct and_<B1>   : public B1   { };  template<typename B1, typename B2>   struct and_<B1, B2>   : public std::conditional<B1::value, B2, B1>::type   { };  template<typename B1, typename B2, typename B3, typename... Bn>   struct and_<B1, B2, B3, Bn...>   : public std::conditional<B1::value, and_<B2, B3, Bn...>, B1>::type   { }; 
like image 21
Jonathan Wakely Avatar answered Sep 19 '22 00:09

Jonathan Wakely