Suppose you have a C++ parameter pack and you want to pluck out an item from it by index.
template<int index, typename...Args>
void example(Args&&... args)
{
// how do I access the index'th args parameter?
}
One solution is to use std::tie:
template<int index, typename...Args>
void example(Args&&... args)
{
auto& arg = std::get<index>(
std::tie(args...));
}
We expand the parameter pack into the parameters of std::, which returns a tuple of lvalue references. We can then pull out the element at the specified index via std::.
The catch here is that std:: always captures the references as lvalue references. If you want to preserve the reference category of the original parameters, you can use std:::
template<int index, typename...Args>
void example(Args&&... args)
{
auto&& arg = std::get<index>(
std::forward_as_tuple(
std::forward<Args>(args)...));
}
This time, we apply std:: to each of the elements of the parameter pack (to preserve the original reference category), then pass them all as parameters to std::. Unlike std::, std:: preserves the reference categories of its arguments, so it produces a tuple of lvalue and rvalue references that match the original parameters. Again, we pull out the element via std::, and capture it as an auto&& reference, which again preserves the reference category.
For future reference, you can capture the Args at a specific index by extracting it from the tuple, or more easily, getting it from the variable.
template<int index, typename...Args>
void example(Args&&... args)
{
auto&& arg = std::get<index>(
std::forward_as_tuple(
std::forward<Args>(args)...));
// The hard way
using Arg = std::tuple_element_t<index,
std::tuple<Args&&...>>;
// The lazy way
using Arg = decltype(arg);
}
Bonus chatter: There is a C++26 proposal to add core language support for pulling elements out of a parameter pack by index: Pack Indexing. Under the proposed syntax, the above code would be simplified to
template<int index, typename...Args>
void example(Args&&... args)
{
// Extract as lvalue
auto& arg = args...[index];
using Arg = Args...[index];
// Preserve reference category
auto&& arg = (Args...[index]&&)args...[index];
using Arg = Args...[index]&&;
}
Could you please explain why the new language rules are designed in such a way that the cast is required to preserve reference category in
<code>
I would prefer
<code>
to have the same effect. See my godbolt experiments