patterncppModerate
Standard-layout tuple implementation
Viewed 0 times
implementationtuplelayoutstandard
Problem
I have written a simple (read: incomplete) tuple implementation that, AFAICT, does not violate the standard layout requirements. It's largely based on Nish's implementation, but I use a recursive hierarchy of nested members, rather than recursive inheritance. Unfortunately this approach necessitates recursion for element access. But for my purposes I am willing to sacrifice compilation time for a predictable memory layout... my tuple stores elements in the "correct" order.
Aside from lengthy compilation times, are there any obvious flaws to this approach?
```
#include
#include
template
struct tuple;
template
struct tuple
{
T first;
tuple rest;
};
template
struct tuple
{
T first;
};
namespace detail {
template
struct tuple_element;
template
struct tuple_element >
: tuple_element >
{};
template
struct tuple_element >
{
using type = T;
};
template
struct tuple_accessor
{
template
static inline typename tuple_element >::type & get (tuple & t)
{
return tuple_accessor::get(t.rest);
}
template
static inline const typename tuple_element >::type & get (const tuple & t)
{
return tuple_accessor::get(t.rest);
}
};
template <>
struct tuple_accessor
{
template
static inline typename tuple_element >::type & get (tuple & t)
{
return t.first;
}
template
static inline const typename tuple_element >::type & get (const tuple & t)
{
return t.first;
}
};
template
struct tuple_builder
{
static inline void make (tuple::type, typename ::std::decay::type...> & t, T && x, Ts &&... xs)
{
t.first = x;
tuple_builder::make(t.rest, ::std::forward(xs)...);
}
};
template
struct tuple_builder
{
static inline void make
Aside from lengthy compilation times, are there any obvious flaws to this approach?
```
#include
#include
template
struct tuple;
template
struct tuple
{
T first;
tuple rest;
};
template
struct tuple
{
T first;
};
namespace detail {
template
struct tuple_element;
template
struct tuple_element >
: tuple_element >
{};
template
struct tuple_element >
{
using type = T;
};
template
struct tuple_accessor
{
template
static inline typename tuple_element >::type & get (tuple & t)
{
return tuple_accessor::get(t.rest);
}
template
static inline const typename tuple_element >::type & get (const tuple & t)
{
return tuple_accessor::get(t.rest);
}
};
template <>
struct tuple_accessor
{
template
static inline typename tuple_element >::type & get (tuple & t)
{
return t.first;
}
template
static inline const typename tuple_element >::type & get (const tuple & t)
{
return t.first;
}
};
template
struct tuple_builder
{
static inline void make (tuple::type, typename ::std::decay::type...> & t, T && x, Ts &&... xs)
{
t.first = x;
tuple_builder::make(t.rest, ::std::forward(xs)...);
}
};
template
struct tuple_builder
{
static inline void make
Solution
Minor issues:
-
You don't support empty tuples. It's obviously not a huge problem, but adding one line
is no great burden either.
-
You should be using std::forward
(Code demo with suggested changes and the now-unnecessary builder templates removed)
- You need to include `
forstd::forward.
-
You don't support empty tuples. It's obviously not a huge problem, but adding one line
template <> struct tuple<> {};is no great burden either.
-
You should be using std::forward
for the assignments in the make functions:
t.first = std::forward(x);
so move assignment is used when x is an rvalue reference.
Slightly bigger issue:
-
Having no constructors in tuple means that your tuple can never contain a type that is not default constructible. "Default construction followed by assignment" is inherently less efficient than value construction. I would write perfect forwarding constructors for the tuple specializations, something like:
template
struct tuple
{
T first;
tuple rest;
tuple() = default;
template ::type
>::value
>::type
>
tuple(U&& u, Us&&...tail) :
first(::std::forward(u)),
rest(::std::forward(tail)...) {}
};
template
struct tuple
{
T first;
tuple() = default;
template ::type
>::value
>::type
>
tuple(U&& u) :
first(::std::forward(u)) {}
};
(All that enable_if junk is to keep the perfect forwarding constructors from being used for copy/move construction from another tuple.) This allows make_tuple` to become a one-liner:return tuple::type...>(::std::forward(x)...);(Code demo with suggested changes and the now-unnecessary builder templates removed)
Code Snippets
template <> struct tuple<> {};t.first = std::forward<T>(x);template <class T, class... Ts>
struct tuple<T, Ts...>
{
T first;
tuple<Ts...> rest;
tuple() = default;
template <class U, class...Us, class=
typename ::std::enable_if<
!::std::is_base_of<
tuple,
typename ::std::decay<U>::type
>::value
>::type
>
tuple(U&& u, Us&&...tail) :
first(::std::forward<U>(u)),
rest(::std::forward<Us>(tail)...) {}
};
template <class T>
struct tuple<T>
{
T first;
tuple() = default;
template <class U, class=
typename ::std::enable_if<
!::std::is_base_of<
tuple,
typename ::std::decay<U>::type
>::value
>::type
>
tuple(U&& u) :
first(::std::forward<U>(u)) {}
};return tuple<typename ::std::decay<Ts>::type...>(::std::forward<Ts>(x)...);Context
StackExchange Code Review Q#52272, answer score: 10
Revisions (0)
No revisions yet.