Variadic Templates
Flexibility and checking cost come from one design choice. A library component that gains generality through variadic parameters gains reuse, and it also takes on the work of describing its valid interface in template code.
A Template That Does Not Fix Its Argument Count
Suppose you want one piece of code that works whether a caller passes one value, five values, or none at all. A template that always expects the same fixed list of parameters cannot do this without help. Variadic templates, sometimes just called variadics, solve the problem by accepting a varying number of arguments. That flexibility is the reason to use them, but it also creates a cost that you must understand before using them well.
What do you think happens?
A template declares exactly two parameters. How many arguments can a caller pass to it?
Reveal answer
Exactly two. That version can only be used with two arguments.
A fixed template's declaration sets its argument count. A variadic template is different: its declaration does not fix the count, so the count is decided by each call.
A Single Slot for a Whole Argument Line
A helpful way to picture a variadic template is as a single slot that takes a whole line of arguments, rather than a row of labeled boxes. The key word in the idea is any. A variadic template is not limited to one specific argument count. It can be given two arguments, or ten, or none, as long as the arguments are acceptable to it.
The arguments in this made-up call do not need to share a type. One is a whole number, one is a character, and one is a decimal value. The number of positions is decided by the call, not by the template's declaration. The diagram above is a made-up call used only to show how the positions line up.
Why the Same Template Takes Different Calls
If the same template were called with only one argument, the list would have a single position. If it were called with none, the list would be empty. In each case the template is the same; only the call changes. This separation between the template and the particular call is what gives variadic templates their reach.
One template, three calls
A single variadic template is called three different ways. For each call, determine how many positions the argument list has and whether the arguments must share a type.
- 1Call 1: one argument
The call passes a single value, so the argument list has one position. The template itself has not changed.
- 2Call 2: three arguments
The call passes 1, a and 2.5. The list now has three positions, holding values of different types.
- 3Call 3: no arguments
The call passes nothing, so the argument list is empty and has no positions.
- 4Compare the calls
The declaration never changed across the three calls. Only the number of arguments supplied by each call changed.
The argument count comes from each call. The template declaration stays the same, and the arguments do not need to share one type.
The Strength and the Weakness Together
The central statement about variadic templates has two halves. Their strength is that they can accept any arguments you care to give them. Their weakness is that checking the types of their interface can be a possibly elaborate template program. These two halves describe the same design choice from two angles, so they should be understood together rather than as separate facts.
Mistakes Beginners Make with Variadics
Common mistake Assuming every argument must have the same type
What it looks likeA call passes a whole number, a character and a decimal value in one argument list.
Why it failsThe arguments do not need to share a type. The number of positions is decided by the call, and the positions are not forced into one type.
Do this insteadRead the argument list as a line of values that may differ in kind, and check each one against the rules of the interface.
Common mistake Assuming a variadic template accepts any arguments at all without checking
What it looks likeA caller passes many values to a variadic template and expects it to work regardless of what they are.
Why it failsThe template accepts any arguments you care to give it, as long as the arguments are acceptable to it. The openness of the call is paired with checking logic inside the template.
Do this insteadTreat the template's checking logic as the place where acceptable arguments are defined.
Common mistake Treating the flexibility as free
What it looks likeA developer adds a variadic parameter to a template and assumes its interface is as easy to verify as a fixed one.
Why it failsThe openness that lets a caller pass any arguments forces the template to carry more of the burden of deciding what counts as a valid interface.
Do this insteadBudget time for writing and understanding the checking logic whenever a template accepts a varying number of arguments.
Common mistake Confusing the template with a particular call: the template stays the same while each call supplies its own number of arguments.
Why the Standard Library Relies on Them
Because of their flexibility, variadic templates are widely used in the standard library. The reasoning is direct. A library is meant to serve many different callers, and those callers will not all want to pass the same number of arguments. A component that accepts any arguments can be reused in more situations than one that fixes its argument count in advance.
The same connection explains why the weakness is worth studying. The components that benefit most from flexibility are exactly the ones whose interfaces have to be checked carefully. When you read library-style code, its generality was bought with template logic that the designer had to write.
Flexibility and checking cost come from one design choice. A library component that gains generality through variadic parameters gains reuse, and it also takes on the work of describing its valid interface in template code.
Practice Tracing a Variadic Call
A variadic template is called with four arguments: the whole number 7, the character x, the decimal value 0.5, and the whole number 9. Describe the argument list the template receives. How many positions does it have, and does the template's declaration need to change for this call compared with a call that passes only one argument?
Hints
- Count the arguments in the call, since the call sets the number of positions.
- Remember that the arguments do not need to share a type.
- Think about whether the template is the same across calls with different counts.
Explain in your own words why checking a variadic template's interface is harder than checking a fixed one, and name the two places where the rules for valid calls can live.
Hints
- Consider what the caller is allowed to supply in each case.
- Think about where the template describes which calls are acceptable.
Key Takeaways
- A variadic template accepts any number of arguments, and the argument count is set by each call rather than by the declaration.
- The same template can receive one argument, several arguments of different types, or none at all.
- The flexibility that lets callers pass any arguments is the same choice that makes checking the interface a possibly elaborate template program.
- Variadic templates are widely used in the standard library because a library must serve callers who want different argument counts.