This is a text-only version of the following page on https://raymii.org:
---
Title : C++ variadic template recursive example
Author : Remy van Elst
Date : 08-06-2019
URL : https://raymii.org/s/snippets/Cpp_variadic_template_recursive_example.html
Format : Markdown/HTML
---
In this article I'll show you how to use a variadic template in C++. Variadic
templates allow you to have a template with a variable number of arguments,
also called a parameter pack. Unpacking that pack is more difficult than it
should be, so we use a recursive template to iterate over all the parameters
one by one. I've also included an example in Python to compare to.
Recently I removed all Google Ads from this site due to their invasive tracking, as well as Google Analytics. Please, if you found this content useful, consider a small donation using any of the options below:
I'm developing an open source monitoring app called Leaf Node Monitoring, for windows, linux & android. Go check it out!
Consider sponsoring me on Github. It means the world to me if you show your appreciation and you'll help pay the server costs.
You can also sponsor me by getting a Digital Ocean VPS. With this referral link you'll get $200 credit for 60 days. Spend $25 after your credit expires and I'll get $25!
### Variadic templates
Variadic templates allow you to have a template with a variable number of
arguments, also called a parameter pack. They were introduced in C++ 11,
before that you had to use `va_` macros with the ellipsis `(...)` operator,
which is not type-safe and quite complex.
My use case was to have a template that allows an arbitrary number of arguments,
of a few different types which will all be processed one by one sequentially. If
you're looking to get all of the template pack and to something with it at
once, this guide isn't for you. That requires either an `initializer list` or a
`tuple`.
[This][1] is a great article on variadic templates with more examples.
[Wikipedia][2] also has a page with some examples. [This][3] is also a good
introduction.
Quoting Kevin from the last linked article:
> When it comes to handling variadic functions, you can't think in the standard
`iterative` C++ style. You need to write such functions recursively, with a
`base` case, and a `recursive` case that reduces, eventually, into a `base`
case. This implies a separate function for each case.
It took me a while to find out how to use the argument pack. At the end of this
article is a comparison to Python, which is what I was used to before going into
C++. There, you can use `Foo(*args)` or `Foo(**kwargs)` and a `for` loop. In
C++ that for loop isn't easily possible.
Quoting [davmac][5] from lobste.rs who has an explanation on why this is not as
easy as I would hope:
> C++ for loops were traditionally a run-time loop and so they don't apply to
parameter packs which are a compile-time construct only. While a for loop can
now be evaluated at compile time (in a constexpr function) their syntax and
semantics are unchanged in that case, i.e. they still apply to constructs that
are (or at least which can also be) run-time. Allow for loops to iterate through
parameter packs would require new syntax and semantics in a language that many
feel is already getting too complex. That said I suspect you could do this with
something more like a regular loop with C++2x features (or possibly even C++17,
I haven't been keeping up to well) e.g. with template lambdas.
Unpacking the pack to a `std::tuple` is possible, but tricky to use afterwards.
By using the ellipsis `(...)` operator in the correct place (left or right of a
parameter name) we can control what happens.
Placing the ellipsis to the left of the parameter name declares an parameter
pack. You use this in the template declaration, like so:
template
void Foo(First first, Args... args) { }
Placing the ellipsis to the right of the parameter will cause the whole
expression that precedes the ellipsis to be repeated for every subsequent
argument unpacked from the argument pack. In our example, it is used in the
variadic function to call the base function:
Foo(args...);
The below `Foo()` example is recursive. The template function with `First` and
`Args...` calls the template with `Arg`, which performs the actual action we
want. Both functions have the same name, thus being overloaded. There also is a
function (not template) which takes no arguments, but that is up to you if you
need that. The functions could be named different (`Base(Arg)` and
`Other(First, Args...)` e.g.).
The `First` argument is required to get the 'One or more' behaviour. If you
would omit it, `Foo(Args...)` would accept zero or more parameters.
### void Foo() example
Tested with `CLion` in C++ 11 mode.
// non template function to call with zero arguments
void Foo() {
std::cout << " ";
}
// base template with 1 argument (which will be called from the variadic one).
template
void Foo(Arg arg) {
//std::cout << __PRETTY_FUNCTION__ << "\n";
std::cout << arg << " ";
}
// variadic template with one or more arguments.
// ellipsis (...) operator to the left of the parameter name declares a parameter pack,
// allowing you to declare zero or more parameters (of different types).
template
void Foo(First first, Args... args) {
//std::cout << __PRETTY_FUNCTION__ << "\n";
Foo(first);
Foo(args...);
// ellipsis (...) operator to the right of the parameter name will cause
// the whole expression that precedes the ellipsis to be repeated for every
// subsequent argument unpacked from the argument pack, with the expressions
// separated by commas.
}
int main() {
std::string one = "One";
const char* two = "Two";
float three = 3.3333333333;
Foo(); // non template
std::cout << std::endl;
Foo(one); // base template
std::cout << std::endl;
Foo(one, two); // variadic argument template
std::cout << std::endl;
Foo(one, two, three); // variadic argument template
std::cout << std::endl;
Foo(1, 2, three, 4, 5.7, 6/2, "lalala"); // variadic argument template
return 0
}
Example output:
One
One Two
One Two 3.33333
1 2 3.33333 4 5.7 3 lalala
### PRETTY_FUNCTION
__PRETTY_FUNCTION__
contains the name of the current function as a string, and
for C++ functions (classes, namespaces, templates and overload) it contains the
`pretty` name of the function including the signature of the function. It is a
`gcc` [extension][4] that is mostly the same as
__FUNCTION__
or
__func__
By placing
std::cout << __PRETTY_FUNCTION__ << "\n"
at the top of the
function, you can get an overview of what is called and when. Consider the
following example:
template
void Foo(Arg arg) {
std::cout << __PRETTY_FUNCTION__ << "\n";
}
template
void Foo(First first, Args... args) {
std::cout << __PRETTY_FUNCTION__ << "\n";
Foo(first);
Foo(args...);
}
int main() {
std::string one = "one";
const char* two = "two";
Foo(one); // base template
std::cout << std::endl;
Foo(one, two); // variadic argument template
std::cout << std::endl;
}
Will output:
void Foo(Arg) [with Arg = std::__cxx11::basic_string]
void Foo(First, Args ...) [with First = std::__cxx11::basic_string; Args = {const char*}]
void Foo(Arg) [with Arg = std::__cxx11::basic_string]
void Foo(Arg) [with Arg = const char*]
The first line is the base template. After the newline, the variadic template
is called and that calls the base template twice.
### Python
In Python preceding a method parameter with an asterisk (`*args`) defines it as
a variable non-keyword list of arguments. Preceding with two asterisks
(`**kwargs`) defines the parameter as a keyworded list of arguments. The
parameters can be named anything as long as the asterisks are there, but
convention says to use `*args` and `**kwargs`.
A small example of the above `Foo()` method in Python. Newline printing is
omited by appending the comma (`,`) to the `print()` function.
#!/usr/bin/python
def Foo(first, *argv):
print(first),
print(" "),
for arg in argv:
print(arg),
print(" "),
print("")
bla = "Hello"
Foo('one')
Foo('one', 'two')
Foo('Remy', 2, 2.4, bla)
Output:
$ python test.py
one
one two
Remy 2 2.4 Hello
An example using keyworded args (`**kwargs`):
#!/usr/bin/python
def Foo2(**kwargs):
if kwargs:
for key, value in kwargs.iteritems():
print("%s: %s, ") % (key,value),
print("")
bla = "Hello"
Foo2(first='one')
Foo2(first='one', second='two')
Foo2(first='one', second='two', three=3, var=bla)
Output:
first: one,
second: two, first: one,
var: Hello, second: two, three: 3, first: one,
[1]: http://web.archive.org/web/20190608070158/https://eli.thegreenplace.net/2014/variadic-templates-in-c/
[2]: https://en.wikipedia.org/wiki/Variadic_template
[3]: http://web.archive.org/web/20190608183836/https://kevinushey.github.io/blog/2016/01/27/introduction-to-c++-variadic-templates/
[4]: https://gcc.gnu.org/onlinedocs/gcc/Function-Names.html
[5]: http://web.archive.org/web/20190613053209/https://lobste.rs/s/qswclv/variadic_templates_vs_kwargs_c_makes_it
---
License:
All the text on this website is free as in freedom unless stated otherwise.
This means you can use it in any way you want, you can copy it, change it
the way you like and republish it, as long as you release the (modified)
content under the same license to give others the same freedoms you've got
and place my name and a link to this site with the article as source.
This site uses Google Analytics for statistics and Google Adwords for
advertisements. You are tracked and Google knows everything about you.
Use an adblocker like ublock-origin if you don't want it.
All the code on this website is licensed under the GNU GPL v3 license
unless already licensed under a license which does not allows this form
of licensing or if another license is stated on that page / in that software:
This program is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation, either version 3 of the License, or
(at your option) any later version.
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with this program. If not, see .
Just to be clear, the information on this website is for meant for educational
purposes and you use it at your own risk. I do not take responsibility if you
screw something up. Use common sense, do not 'rm -rf /' as root for example.
If you have any questions then do not hesitate to contact me.
See https://raymii.org/s/static/About.html for details.