Получить новый кортеж, содержащий все элементы, кроме первого - PullRequest
0 голосов
/ 27 ноября 2018

Учитывая std::tuple<A, B, ...> foo, есть ли какая-либо универсальная (шаблонная) функция или методика в C ++ 14 для получения нового кортежа std::tuple<B, ...> bar, который содержит все элементы, кроме первого foo?Или, может быть, что-то в Boost?

Я написал вспомогательную функцию, используя пакеты параметров и некоторые шаблоны метапрограммирования, чтобы сделать это, но я бы хотел выбросить все эти вещи!

Вот что я сейчас делаю.Я определяю вспомогательную функцию unshift_tuple(), которая возвращает кортеж, содержащий все, кроме первого элемента кортежа, переданного в функцию.Реализация unshift_tuple() использует вспомогательную функцию unshift_tuple_with_indices(), которая принимает пакет параметров, содержащий индексы кортежа, для извлечения;вспомогательный тип sequential_integer_list используется для генерации соответствующего пакета параметров списка индексов с использованием шаблонного метапрограммирования.Гадкий!

#include <tuple>

template <size_t... Integers>
struct integer_list {};

template <size_t N, size_t... Args>
struct sequential_integer_list : sequential_integer_list<N - 1, N - 1, Args...> {};

template <size_t... Args>
struct sequential_integer_list<0, Args...> { typedef integer_list<Args...> type; };

template <typename FirstElement, typename... Elements, size_t... Indices>
static std::tuple<Elements...> unshift_tuple_with_indices(
    const std::tuple<FirstElement, Elements...>& tuple,
    integer_list<Indices...> index_type)
{
    return std::make_tuple(std::get<Indices + 1>(tuple)...);
}

template <typename FirstElement, typename... Elements>
std::tuple<Elements...>
unshift_tuple(const std::tuple<FirstElement, Elements...>& tuple)
{
    return unshift_tuple_with_indices(tuple,
        typename sequential_integer_list<sizeof...(Elements)>::type());
}

int main(int, char *[])
{
    std::tuple<int, std::string, double> foo(42, "hello", 3.14);

    std::tuple<std::string, double> bar = unshift_tuple(foo);
}

Для ясности, этот код работает просто отлично.Я просто очень хочу удалить его (или любую его часть) и использовать вместо него что-нибудь встроенное, если это возможно!


Edit

Jarod42 указал на существование std::integer_list в C ++ 14, который упрощает реализацию до чего-то вроде:

#include <cstddef>
#include <tuple>
#include <utility>

template <typename T1, typename... T, size_t... Indices>
std::tuple<T...> unshift_tuple_with_indices(
    const std::tuple<T1, T...>& tuple, std::index_sequence<Indices...>)
{
    return std::make_tuple(std::get<Indices + 1>(tuple)...);
}

template <typename T1, typename... T> std::tuple<T...>
unshift_tuple(const std::tuple<T1, T...>& tuple)
{
    return unshift_tuple_with_indices(tuple,
        std::make_index_sequence<sizeof...(T)>());
}

Ответы [ 2 ]

0 голосов
/ 27 ноября 2018

В C ++ 17 вы можете сделать

template <typename T1, typename... Ts>
std::tuple<Ts...> unshift_tuple(const std::tuple<T1, Ts...>& tuple)
{
    return std::apply([](auto&&, const auto&... args) {return std::tie(args...);}, tuple);
}

std::apply, может быть реализовано в C ++ 14.

Иначе, в C ++14, есть std::index_sequence, которые избегают писать свою собственную версию, что упрощает ваш код до чего-то вроде:

namespace details
{
    template <typename Tuple, size_t... Indices>
    auto unshift_tuple_with_indices(
        const Tuple& tuple,
        std::index_sequence<Indices...> index_type)
    {
        return std::make_tuple(std::get<Indices + 1>(tuple)...);
    }
}

template <typename T, typename... Ts>
std::tuple<Ts...> unshift_tuple(const std::tuple<T, Ts...>& tuple)
{
    return details::unshift_tuple_with_indices(tuple, std::index_sequence_for<Ts...>());
}
0 голосов
/ 27 ноября 2018

В C ++ 14 есть шаблон класса с именем std::integer_sequence, который похож на то, что вы делаете с sequential_integer_list.Вы можете найти ссылку в здесь .

В свете вашего кода, можно сократить и положиться на чистое метапрограммирование.

template <typename First, typename ... Elements>
struct unshift_tuple_impl
{
    using type = std::tuple<Elements...>;
};

template <typename First, typename ... Elements>
std::tuple<Elements...>
unshift_tuple(const std::tuple<First, Elements...>& tuple)
{
    return typename unshift_tuple_impl<First, Elements...>::type{};
}
...