C++11 variable number of arguments, same specific type
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});
}
Is there a way to define a variadic number of arguments of the same type?
I can't figure out how to implement a function with a variable number of arguments of the same type.
Template argument of the same type or ordinary function arguments of the same type?
The first case is simple (if the type is one admitted for template value types), exactly as you have written
template<S* s, int... args>
fun (int arg1, int arg2);
and you can use they using template folding, if you can use C++17,
template <S* s, int... args>
auto fun (int arg1, int arg2)
{ ((s->r1 += 7 * args), ...); }
or in a little more complicated way before (C++11/C++14)
template <S* s, int... args>
auto fun (int arg1, int arg2)
{
using unused = int[];
(void)unused { 0, s->r1 += 7 * args ... };
}
Unfortunately you can call this type of function with compile time known integers so, by example, not with variables
int a = 7;
fun<&s,1,2,a,4>(mode,speed); // compilation error
In this case you need a variadic list of ordinary function arguments of the same type; unfortunately this is a little more complicated.
You can create a typical variadic list of template parameter
template <typename ... Args>
auto fun (Args ... args)
imposing, through SFINAE, that all Args...
are deduced or explicated as int
(see Michael Kenzel's answer).
Unfortunately this require that every argument is exactly if type int
so calling func with (by example) a long int
gives a compilation error
fun(1, 2, 3l); // compilation error (3l is a long int, not an int)
Obviously you can relax the SFINAE condition imposing (by example) that all Args...
types are convertible (std::is_convertible
) to int
but isn't exactly has developing a function receiving a variadic number of arguments of the same type.
If you can accept a superior limit to the number of arguments (64
, in the following example) and that the function is method (maybe static) of a class, you can create a foo
class containing a method f()
that receive zero int
, one f()
that receive one int
, one f()
that receive two int
s, etc, until an f()
that receive 63 int
s.
The following is a full compiling C++17 example
#include <utility>
#include <type_traits>
struct S
{
int r1;
int r2;
};
S s;
const int mode=3, speed=1;
template <typename T, std::size_t>
using getType = T;
template <std::size_t N, typename = std::make_index_sequence<N>>
struct bar;
template <std::size_t N, std::size_t ... Is>
struct bar<N, std::index_sequence<Is...>>
{
static constexpr auto f (getType<int, Is> ... args)
{ ((s.r1 += 7 * args), ...); }
};
template <S &, std::size_t N = 64u, typename = std::make_index_sequence<N>>
struct foo;
template <S & s, std::size_t N, std::size_t ... Is>
struct foo<s, N, std::index_sequence<Is...>> : public bar<Is>...
{ using bar<Is>::f...; };
int main ()
{
foo<s>::f(mode, speed);
}
In C++14 is a little more complicated because there isn't variadic using
so you have to write the foo
class in a recursive way.
In C++11 you have also to develop a substitute for std::make_index_sequence
/std::index_sequence
.
Can I create a function which takes any number of arguments of the same type?
Use C++17 fold expression:
template<class... Args>
constexpr auto total_sum(const Args&... args) {
return (args + ... + 0);
}
static_assert(total_sum(1, 2, 5, 4, 2) == 14);
static_assert(total_sum(3, 5, 6, 2) == 16);
variable number of arguments, same specific type without macro or initializer-list
{}
requires you are initializing something of a specific type.
C++11 variable arguments requires that your types be deducted types.
These are opposing requirements.
Now I could generate an object with a set of ()
overloads up to some large finite number.
namespace details {
template<std::size_t, class T>
using ignore_index=T;
template<class T, class Count, class Base>
struct linear_overload_count;
template<class T, std::size_t I0, std::size_t...Is, class Base>
struct linear_overload_count<T, std::index_sequence<I0,Is...>, Base>:
linear_overload_count<T, std::index_sequence<Is...>, Base>
{
using linear_overload_count<T, std::index_sequence<Is...>, Base>::operator();
using linear_overload_count<T, std::index_sequence<Is...>, Base>::linear_overload_count;
std::result_of_t<
Base const&(T const&, ignore_index<Is,T>const&...)
>
operator()(T const& t0, ignore_index<Is,T>const&...ts) const {
return Base::operator()(t0, ts...);
}
linear_overload_count()=default;
linear_overload_count(linear_overload_count const&)=default;
linear_overload_count(linear_overload_count &&)=default;
linear_overload_count& operator=(linear_overload_count const&)=default;
linear_overload_count& operator=(linear_overload_count &&)=default;
};
template<class T, class Base>
struct linear_overload_count<T, std::index_sequence<>, Base>:
Base
{
using Base::Base;
linear_overload_count(Base&& b):Base(std::move(b)) {}
linear_overload_count(Base const& b):Base(b) {}
std::result_of_t<
Base const&()
>
operator()() const {
return Base::operator()();
}
linear_overload_count()=default;
linear_overload_count(linear_overload_count const&)=default;
linear_overload_count(linear_overload_count &&)=default;
linear_overload_count& operator=(linear_overload_count const&)=default;
linear_overload_count& operator=(linear_overload_count &&)=default;
};
}
template<class T, std::size_t N, class Base>
using linear_overload_Ts = details::linear_overload_count<T, std::make_index_sequence<N>, Base>;
auto count_args_impl = [](auto&&...args) { std::cout << sizeof...(args) << "\n"; };
struct bob {
int x,y;
};
using count_bobs_t = linear_overload_Ts< bob, 3, decltype(count_args_impl) >;
count_bobs_t const bobs = count_args_impl;
int main() {
bobs();
bobs({}, {}, {1,2});
}
live example.
Now we can have up to 100 overloads in bobs
by changing the number 3
above to 100
.
Note that if you hit a more than a few 100, your compiler will die. This can be fixed with a binary tree inheritance instead of a linear one, but I cannot be bothered.
In addition, this technique can slow down compiling.
Note that Base
must be a type. You can use a lambda like above to forward to your template function (give them different names), a manual function object, or whatever else.
Using this technique to create a function instead of a function object isn't something I can solve without the help of a type being named in the call (so, use ADL to find the generated function). Function objects don't participate in overload resolution in the same way that functions do, which may be a problem.
This also seems like a lot of work to do away with adding an extra set of {}
.
A function with variable number of arguments with known types, the c++11 way
It is straight forward to write a function with variadic templates, that accept an arbitrary number of arguments. The only difference to the general pattern is, that a concrete type is used as first argument (head) - instead of a template parameter. The following example shows a function foobar
, that accepts an arbitrary number of strings.
// used for end of recursion - and for the empty arguments list
void foobar() { }
template <typename ...Tail>
void foobar(const std::string& head, Tail&&... tail)
{
// do something with head
std::cout << head << '\n';
// call foobar recursively with remaining arguments
foobar(std::forward<Tail>(tail)...);
}
foobar("Hello", "World", "...");
Personally, I prefer using std::initializer_list
instead of variadic templates. Because variadic templates are more complex and require additional experience. With std::initializer_list
, it might look like this:
void foobar(std::initializer_list<std::string> values)
{
for (auto& value : values) {
// do something with value
std::cout << value << '\n';
}
}
foobar({ "Hello", "World", "...", });
Unfortunately, the additional curly braces are required when using std::initializer_list
with regular functions. They are not required for constructors, if the new initializer syntax is used.
Edit: Rewrote the answer according to the feedback. In particular I have changed the order of the two solutions/examples.
Using a function with a variable number of arguments
You can use variadic templates. One example for printing out all the passed argument is given below:
#include <iostream>
//provide an ordinary function to end recursion
void print ()
{
}
template<typename T, typename... Types>
void print (T firstArg, Types... args)
{
std::cout << firstArg << "\n"; // printing the very first argument passed
print(args...); // printing the rest of the argument by calling print()
}
int main()
{
print(1, 2, 3, "some string literal");//call function template print() with arguments `1`, `2`, `3` and `"some string literal"`
return 0;
}
In the above snippet, if we call print()
with one or more arguments then the templated version of print
will be used/called which just prints the very first argument passed(which is 1
in my example) using cout
and then calls print
with the remaining arguments(which are 2
, 3
and "some string literal
.
Now the whole process repeats. In particular, the very first argument 2
is printed using cout
and then print
is called with the remaining arguments(3
, "some string literal")
.
This goes on until there are no more arguments to pass and in this case when print
is called with no arguments then the ordinary non-template function print
will be used/chosen, thus ending the recursion.
The output of the above program can be seen here:
1
2
3
some string literal
Note that there are other ways as well like using fold expression(with C++17) to do the same. Also, in the above example, the arguments are passed by value. You can modify the program to pass them by reference according to your needs.
With C++17, you can use fold expression:
#include <iostream>
template<class... Args>
void print(const Args&... args)
{
(std::cout << ... << args) << "\n"; //uses fold expression
}
int main()
{
print(1, 2, 3, "some string literal");
return 0;
}
is there a c++ version of int..args
From C++11 and above, you can use parameter packs and store them in a std::initializer_list
:
#include <iostream>
#include <initializer_list>
template <typename ...Args>
void fun(Args const&... args) {
std::initializer_list<int> arg_list { args... };
std::cout << "Number of arguments: " << arg_list.size() << std::endl;
for (auto const& i : arg_list)
std::cout << i << " ";
std::cout << std::endl;
}
int main() {
// Calling the varargs method with different number
// of parameters
fun(100); // one parameter
fun(1, 2, 3, 4); // four parameters
fun(); // no parameter
// fun(1, ""); // Error
}
Related Topics
Std::Vector Reserve() and Push_Back() Is Faster Than Resize() and Array Index, Why
What Is a "Regular Type" in the Context of Move Semantics
How to Force Gcc to Assume That a Floating-Point Expression Is Non-Negative
Are There Any Downsides to Using Upx to Compress a Windows Executable
Std::Unique_Ptr with Derived Class
Confusion About Pointers and References in C++
How to Print Member Function Address in C++
Brace Elision in Std::Array Initialization
Constexpr Initialization of Array to Sort Contents
Add Library to Visual Studio 2008 C++ Project
Conversion from Boost::Shared_Ptr to Std::Shared_Ptr
C++11 Anonymous Union with Non-Trivial Members
How to Create Std::Array with Initialization List Without Providing Size Directly
Advantages of Classes with Only Static Methods in C++
Lnk2019: Unresolved External Symbol _Main Referenced in Function _Tmaincrtstartup
Comparing 3 Modern C++ Ways to Convert Integral Values to Strings