This blog entry is an experiment: instead of drafting it and asking an LLM to review and improve the prose, I have attempted to use the LLM as a “tutor,” lightly guiding its path and, after the fact, checking the code (and its statements) for syntax correctness and accuracy.
It would be great to hear back from folks whether this format is useful at all.
Follow up questions are delimited by a line separator:

The semantics and use of Variadic Templates in C++
In C++, we have
Variadic Templates: expressions such as:
template<typename... Args>
Res doSomething(Args... args) {
// some code
}
can you please explain to me:
- what is the actual use of the
Args...type and what does it map to (as a C++ type) - how can I access the individual arguments (and is it even possible)
Also, I understand that those args will be expanded to match the actual arguments of the function invocation, but what constraints are there on the types:
- Do they need to be copy-constructible?
- Are there any other rules?
What follows are lightly edited (and code-checked) responses from Meta’s LLama 3
1. What is the actual use of the Args... type and what does it map to (as a C++ type)?
In C++, Args... is a parameter pack, which is a feature introduced in C++11 as part of the variadic templates extension. A parameter pack is a way to represent a variable number of template arguments or function parameters.
In your example, Args... is a template parameter pack that represents a variable number of types. When you instantiate the template with a specific set of types, the compiler will expand the parameter pack into a sequence of types.
For example, if you have:
template<typename... Args>
Res doSomething(Args... args) {
// ...
}
// Instantiation with three types: int, float, and double
Res result = doSomething<int, float, double>(1, 2.0f, 3.0);
The compiler will expand the Args... parameter pack into a sequence of three types: int, float, and double.
2. How can I access the individual arguments (is it even possible)?
Yes, it is possible to access individual arguments from the parameter pack. You can use the std::get function from the <utility> header to access an argument at a specific index.
For example:
#include <utility>
template<typename... Args>
Res doSomething(Args... args) {
// Access the first argument
auto first_arg = std::get<0>(args);
// Access the second argument
auto second_arg = std::get<1>(args);
// ...
}
Note that the index of the argument starts from 0.
3. Rules for expanding the parameter pack
When you instantiate a template with a parameter pack, the compiler will expand the pack according to the following rules:
- Each argument in the pack is treated as a separate template argument.
- If an argument is a pack expansion (i.e., another parameter pack), it is recursively expanded.
- The resulting expanded pack is treated as a single template argument.
Regarding the requirements for the types in the parameter pack:
- They must be copy-constructible (or move-constructible in C++11).
- They must be assignable (or move-assignable in C++11).
These requirements are due to the fact that the compiler needs to create copies of the arguments when expanding the parameter pack.
Accessing the Parameter Pack’s arguments
You said:
“Yes, it is possible to access individual arguments from the parameter pack. You can use the
std::getfunction from the<utility>header to access an argument at a specific index.”Does this mean that the “pack” is, in fact, a
std::tuple?
Could you please expand a bit more about thestd::get<>function?
Is the “pack” a std::tuple?
Yes, when you expand a parameter pack using std::get, it is effectively treated as a std::tuple. The std::get function returns a reference to the element at the specified index in the tuple.
In fact, when you use std::get with a parameter pack, the compiler will automatically create a std::tuple object to hold the expanded arguments. This tuple object is then used to access individual elements using their indices.
std::get<> function
The std::get<> function is a part of the C++ Standard Library, defined in the <utility> header. It provides a way to access an element at a specific index in a std::tuple or a parameter pack.
The syntax for using std::get<> is:
template<std::size_t Index, typename... Types>
constexpr typename std::tuple_element<Index, std::tuple<Types...>>::type&
get(Types&... args);
template<std::size_t Index, typename... Types>
constexpr typename std::tuple_element<Index, std::tuple<Types...>>::type&&
get(Types&&... args);
Here, Index is the index of the element to access (starting from 0), and Types is the parameter pack representing the elements.
The function returns a reference to the element at the specified index. If the element is not present (i.e., the index is out of bounds), the program will terminate with an assertion failure.
You can use std::get<> with both lvalue references (&) and rvalue references (&&) to access elements in the parameter pack.
The semantics of constexpr
what does
constexprdoes, in general and in the expressions above?
What is constexpr?
constexpr is a keyword in C++11 and later versions that indicates that a function or variable is a constant expression. A constant expression is an expression that can be evaluated at compile-time.
When a function is declared constexpr, it means that the function can be evaluated at compile-time if its arguments are known at compile-time. This allows the compiler to optimize the code by evaluating the function at compile-time, reducing the amount of work done at runtime.
Similarly, when a variable is declared constexpr, it means that the variable is a constant that can be evaluated at compile-time.
What does constexpr do in the expressions above?
In the expressions above, constexpr is used to declare the get function as a constant expression. This means that the get function can be evaluated at compile-time if its arguments are known at compile-time.
By declaring the get function as constexpr, the compiler can optimize the code by evaluating the function at compile-time, reducing the amount of work done at runtime. This can result in faster and more efficient code.
Note that for the get function to be evaluable at compile-time, its arguments must also be evaluable at compile-time. This means that the arguments must be constant expressions themselves.
Runtime evaluation of constexpr
In this expression, can the template value (
iin the example) be evaluated at runtime? or does it need to beconstexpr?
int i = readFromStdin();
auto res = std::get<i>(args);
For example, if I want to display one of the
argsvalues, selected by the user?
Can the template value be evaluated at runtime?
No, the template value i in the expression std::get<i>(args) cannot be evaluated at runtime. It must be a compile-time constant.
This is because std::get is a template function that uses template metaprogramming to access the element at the specified index. The index i is used as a template argument, which means it must be known at compile-time.
What if I want to select an element at runtime?
If you want to select an element at runtime based on user input or some other condition, you cannot use std::get with a runtime value.
Instead, you can use a different approach, such as:
- Using a
switchstatement or anif-elsechain to handle each possible index value. - Creating an array or a vector of elements and using the runtime index to access the corresponding element.
- Using a mapping function or a lookup table to map the runtime index to the corresponding element.
Here’s an example of using a switch statement to handle each possible index value:
int index = ...; // runtime index value
auto& arg = std::get<0>(args); // default to first element
switch (index) {
case 0: arg = std::get<0>(args); break;
case 1: arg = std::get<1>(args); break;
case 2: arg = std::get<2>(args); break;
// ...
}
Note that this approach can become cumbersome for large numbers of elements or complex indexing logic. In such cases, using a mapping function or a lookup table may be more efficient.
Upper limit on number of varargs
Is there an upper limit to the number of arguments in a variadic expression, or in a tuple, for that matter?
In other words, hasstd::get<N>(args)an upper bound forN?
Upper limit for variadic template arguments
In C++, the number of arguments in a variadic template is limited by the TEMPLATE_PARAMETER_LIST_MAX macro, which is defined in the <limits> header.
The value of TEMPLATE_PARAMETER_LIST_MAX is implementation-defined, but it must be at least 1024. This means that you can have up to 1024 template arguments in a single template parameter list.
However, it’s worth noting that exceeding this limit will result in a compiler error.
Upper limit for std::get<N>(args)
Since std::get<N>(args) uses a template parameter pack, the upper limit for N is the same as the upper limit for template parameter lists, which is TEMPLATE_PARAMETER_LIST_MAX.
This means that you can use std::get<N>(args) with N values up to TEMPLATE_PARAMETER_LIST_MAX, but exceeding this limit will result in a compiler error.
Practical considerations
While the theoretical limit for N is high, it’s generally not recommended to exceed a few dozen elements in a parameter pack or tuple. The complexity of the code increases exponentially with the number of elements, making it harder to read, write, and debug.
In practice, it’s often better to use other data structures, such as arrays or vectors, when dealing with large numbers of elements.






Leave a comment