Last time, we developed the select_tuple function which takes a tuple and an index sequence and produces a new tuple that selects the elements based on the index sequence. Here’s what we had:
// Don't use this; see discussion.
template<typename Tuple, std::size_t... Ints>
auto select_tuple(Tuple&& tuple, std::index_sequence<Ints...>)
{
return std::make_tuple(
std::get<Ints>(std::forward<Tuple>(tuple))...);
}
The idea is that you can do something like
std::tuple<int, char, float> t{ 1, 'x', 2.0 };
auto t2 = select_tuple(t, std::index_sequence<0, 2>{});
and the result is that t2 is a std::tuple<int, float>{ 1, 2.0 }.
But there’s a problem with this function.
Here’s a riddle: When does std::make_tuple<T>() return something that isn’t a std::tuple<T>?
std::make_tuple<T> |
Produces std::tuple<T> |
|---|---|
int   |
int |
const int   |
|
int&Â Â |
|
int&&Â |
|
std::reference_wrapper<     int > |
int& |
std::reference_wrapper<const int > |
|
std::reference_wrapper<Â Â Â Â Â int&Â > |
|
std::reference_wrapper<Â Â Â Â Â int&&> |
Answer: When T is subject to decay or decays to a reference_wrapper.
Decay is a term in the C++ standard that refers to the changes of type that typically occur when something is passed by value to a function:
- References decay to the underlying type.
- cv-qualifiers (
constandvolatile) are removed. - Arrays decay to pointers.
- Function decay to function pointers.
But make_tuple adds an additional wrinkle: If the decayed type is a reference_wrapper, then the result is the underlying reference.
We don’t want any of these transformations to occur. If you select a type from a tuple that is a reference, then you want the resulting tuple to have the same reference type.
So we can’t use make_tuple. We’ll specify the desired tuple type explicitly.
template<typename Tuple, std::size_t... Ints>
auto select_tuple(Tuple&& tuple, std::index_sequence<Ints...>)
{
return std::tuple<std::tuple_element_t<Ints, Tuple>...>(
std::get<Ints>(std::forward<Tuple>(tuple))...);
}
or alternatively
template<typename Tuple, std::size_t... Ints>
std::tuple<std::tuple_element_t<Ints, Tuple>...>
select_tuple(Tuple&& tuple, std::index_sequence<Ints...>)
{
return { std::get<Ints>(std::forward<Tuple>(tuple))... };
}
Okay, now that we have this helper function, we can do a bunch of fancy tuple manipulation.
Which we’ll do next time.
0 comments