HiveBrain v1.2.0
Get Started
← Back to all entries
patterncppModerate

Standard-layout tuple implementation

Submitted by: @import:stackexchange-codereview··
0
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

Solution

Minor issues:

  • You need to include ` for std::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.