Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
9 changes: 9 additions & 0 deletions docs/tuple.adoc
Original file line number Diff line number Diff line change
Expand Up @@ -145,6 +145,15 @@ auto sum1 = stdx::tuple{"hello"s, "world"s}.join(
[] (auto const &acc, auto const &s) { return acc + ", " + s; }); // "hello, world"
----

`tail` is a member function that returns the tail of a tuple, i.e. every element
from index 1 onwards.
[source,cpp]
----
auto t = stdx::tuple{1, 2, 3}.tail(); // stdx::tuple{2, 3}
----

NOTE: `tail` called on an empty tuple returns an empty tuple.

=== Indexed tuples

Sometimes, it is useful to index a tuple by something other than a plain
Expand Down
27 changes: 27 additions & 0 deletions include/stdx/tuple.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -335,6 +335,33 @@ struct tuple_impl<std::index_sequence<Is...>, index_function_list<Fs...>, Ts...>

constexpr static auto size =
std::integral_constant<std::size_t, sizeof...(Ts)>{};
constexpr static auto empty = std::bool_constant<sizeof...(Ts) == 0>{};

[[nodiscard]] constexpr auto tail() const & {
if constexpr (empty()) {
return *this;
} else {
return [&]<std::size_t... Js>(std::index_sequence<Js...>) {
return tuple_impl<std::index_sequence<Js...>,
index_function_list<Fs...>,
nth_t<Js + 1, Ts...>...>{
(*this)[index<Js + 1>]...};
}(std::make_index_sequence<sizeof...(Ts) - 1>{});
}
}

[[nodiscard]] constexpr auto tail() && {
if constexpr (empty()) {
return *this;
} else {
return [&]<std::size_t... Js>(std::index_sequence<Js...>) {
return tuple_impl<std::index_sequence<Js...>,
index_function_list<Fs...>,
nth_t<Js + 1, Ts...>...>{
std::move(*this)[index<Js + 1>]...};
}(std::make_index_sequence<sizeof...(Ts) - 1>{});
}
}

private:
template <typename Funcs, typename... Us>
Expand Down
15 changes: 15 additions & 0 deletions test/tuple.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -492,3 +492,18 @@ TEST_CASE("indexing unambiguously", "[tuple]") {
STATIC_CHECK(t[0_idx] == 42);
STATIC_CHECK(t[1_idx][0_idx] == 17);
}

TEST_CASE("tuple tail (const lvalue)", "[tuple]") {
constexpr auto t = stdx::tuple{1, 2, 3};
STATIC_CHECK(t.tail() == stdx::tuple{2, 3});
}

TEST_CASE("tuple tail (rvalue)", "[tuple]") {
auto t = stdx::tuple{move_only{42}, move_only{17}};
CHECK(std::move(t).tail() == stdx::tuple{move_only{17}});
}

TEST_CASE("tuple tail (empty tuple)", "[tuple]") {
constexpr auto t = stdx::tuple{};
STATIC_CHECK(t.tail() == stdx::tuple{});
}
5 changes: 5 additions & 0 deletions test/tuple_algorithms.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -63,6 +63,11 @@ TEST_CASE("transform stops at smallest tuple length", "[tuple_algorithms]") {
stdx::tuple{1, 2}) == stdx::tuple{2, 4});
}

TEST_CASE("zip with tail", "[tuple_algorithms]") {
auto t = stdx::tuple{1, 2, 3};
CHECK(stdx::transform(std::plus{}, t, t.tail()) == stdx::tuple{3, 5});
}

namespace {
template <typename Key, typename Value> struct map_entry {
using key_t = Key;
Expand Down