patterncppMinor
A non-recursive tuple_element<index, Tuple> implementation
Viewed 0 times
implementationnontuple_elementrecursivetupleindex
Problem
This is basically a non-recursive
Note
To make this non-recursive, you must replace
How it works
Basically, for
Source code
Convenience aliases
Test case
```
#include
#include
int main()
{
using tuple_t = std::tuple;
static_assert( std::is_same, std::string>::value, "!
std::tuple_element implementation.Note
To make this non-recursive, you must replace
std::make_index_sequence with a non-recursive implementation. I left it with std::make_index_sequence in order to reduce the amount of unrelated code.How it works
deduct has a specialization of deduct_impl that is generated from the index sequence template argument it receives. It is used in order to deduce the type at index in a variadic type template or tuple. It uses the itp_base and itp types.itp and itp is an index-type-pair used to expand the variadic indices template with a type variadic template in order to match the generated specialization.deducer puts it all together by specializing deduct and deduct_impl by using std::conditional_t to generate the correct specialization.Basically, for
std::tuple, in order to get the type at index 1, it creates itp_base, itp, itp_base and passes it to deduct and deduct_impl.Source code
#include
#include
template
struct itp_base {};
template
struct itp : itp_base {};
template
struct deduct;
template
struct deduct>
{
template
struct deduct_impl;
template
struct deduct_impl..., itp, R...>>
{
using type = T;
};
};
template
class deducer
{
private:
static_assert( index
struct deducer_impl;
template
struct deducer_impl>
{
using type = typename deduct
>::template deduct_impl
, itp>::value,
itp,
itp_base
>...
>
>::type;
};
public:
using type = typename deducer_impl>::type;
};Convenience aliases
template
struct tuple_element;
template
struct tuple_element> : deducer {};
template
using tuple_element_t = typename tuple_element::type;Test case
```
#include
#include
int main()
{
using tuple_t = std::tuple;
static_assert( std::is_same, std::string>::value, "!
Solution
Your approach on finding the
But we don't actually need to introduce this
And on the other side, we pass in:
Same idea, just more direct.
For that matter, we don't even need the
and:
A different approach
Another non-recursive way to do this would be through an inheritance tree. We basically take
Then, given a function template constrained on an index, we can pull out the correct type:
I don't know which solution is better, but this one is certainly shorter.
nth element relies on being able to construct a tuple with the first n-1 types being some predictable thing that you can match against. But the types you chose for std::tuple were, for index 1, std::tuple, itp, itp_base>. But we don't actually need to introduce this
itp thing to do this, we can simply turn it into std::tuple. That way, deduct_impl's specialization instead of taking a std::tuple..., itp, R...>, we can simplify to:template
using make_void = void;
template
struct deduct_impl..., T, R...>>
{
using type = T;
};And on the other side, we pass in:
std::tuple...>Same idea, just more direct.
For that matter, we don't even need the
tuple! std::tuple is an expensive type to construct at compile-time, so it's best to avoid it entirely. Just pass in the types as a pack (though the following doesn't compile on gcc - though even there you don't need tuple, just use a light wrapper like template struct typelist;)template
struct deduct_impl;
template
using make_void = void;
template
struct deduct_impl..., T, R...>
{
using type = T;
};and:
using type = typename deduct
>::template deduct_impl
...
>::type;A different approach
Another non-recursive way to do this would be through an inheritance tree. We basically take
std::tuple and turn it into a type that inherits from indexed, indexed, and indexed:template
struct indexed {
using type = T;
};
template
struct indexer;
template
struct indexer, Ts...>
: indexed...
{};Then, given a function template constrained on an index, we can pull out the correct type:
template
struct at_index {
private:
template
static indexed select(indexed);
using impl = indexer, Ts...>;
public:
using type = typename decltype(select(impl{}))::type;
};I don't know which solution is better, but this one is certainly shorter.
Code Snippets
template <std::size_t>
using make_void = void;
template <typename T, typename... R>
struct deduct_impl<std::tuple<make_void<indices>..., T, R...>>
{
using type = T;
};std::tuple<std::conditional_t<(indices == index), Types, void>...>template <typename... Ts>
struct deduct_impl;
template <std::size_t>
using make_void = void;
template <typename T, typename... R>
struct deduct_impl<make_void<indices>..., T, R...>
{
using type = T;
};using type = typename deduct<index, std::make_index_sequence<index>
>::template deduct_impl
<
std::conditional_t<(indices == index), Types, void>...
>::type;template <std::size_t I, typename T>
struct indexed {
using type = T;
};
template <typename Is, typename ...Ts>
struct indexer;
template <std::size_t ...Is, typename ...Ts>
struct indexer<std::index_sequence<Is...>, Ts...>
: indexed<Is, Ts>...
{};Context
StackExchange Code Review Q#112547, answer score: 5
Revisions (0)
No revisions yet.