Удаление первого типа std :: tuple

Это, кажется, очень простой вопрос: как удалить первый (n-й) тип в std::tuple?

Пример:

typedef std::tuple<int, short, double> tuple1;
typedef std::tuple<short, double> tuple2;

Операция, описанная выше, преобразует tuple1 в tuple2, Является ли это возможным?

25

Решение

Вы можете использовать функцию простого типа, основанную на частичной специализации шаблона класса:

#include <type_traits>
#include <tuple>

using namespace std;

template<typename T>
struct remove_first_type
{
};

template<typename T, typename... Ts>
struct remove_first_type<tuple<T, Ts...>>
{
typedef tuple<Ts...> type;
};

int main()
{
typedef tuple<int, bool, double> my_tuple;
typedef remove_first_type<my_tuple>::type my_tuple_wo_first_type;

static_assert(
is_same<my_tuple_wo_first_type, tuple<bool, double>>::value,
"Error!");
}

Кроме того, это решение может быть легко обобщено для удаления я-я тип кортежа:

#include <type_traits>
#include <tuple>

using namespace std;

template<size_t I, typename T>
struct remove_ith_type
{
};

template<typename T, typename... Ts>
struct remove_ith_type<0, tuple<T, Ts...>>
{
typedef tuple<Ts...> type;
};

template<size_t I, typename T, typename... Ts>
struct remove_ith_type<I, tuple<T, Ts...>>
{
typedef decltype(
tuple_cat(
declval<tuple<T>>(),
declval<typename remove_ith_type<I - 1, tuple<Ts...>>::type>()
)
) type;
};

int main()
{
typedef tuple<int, bool, double> my_tuple;
typedef remove_ith_type<1, my_tuple>::type my_tuple_wo_2nd_type;

static_assert(
is_same<my_tuple_wo_2nd_type, tuple<int, double>>::value,
"Error!");
}
28

Другие решения

Я написал предложение который был принят в стандарт C ++ 14, что делает его довольно простым для любого типа типа «кортеж», т.е. тот, который поддерживает tuple_size а также tuple_element API:

template<typename T, typename Seq>
struct tuple_cdr_impl;

template<typename T, std::size_t I0, std::size_t... I>
struct tuple_cdr_impl<T, std::index_sequence<I0, I...>>
{
using type = std::tuple<typename std::tuple_element<I, T>::type...>;
};

template<typename T>
struct tuple_cdr
: tuple_cdr_impl<T, std::make_index_sequence<std::tuple_size<T>::value>>
{ };

И вы можете преобразовать объект кортежа в новый тип только с помощью пары функций:

template<typename T, std::size_t I0, std::size_t... I>
typename tuple_cdr<typename std::remove_reference<T>::type>::type
cdr_impl(T&& t, std::index_sequence<I0, I...>)
{
return std::make_tuple(std::get<I>(t)...);
}

template<typename T>
typename tuple_cdr<typename std::remove_reference<T>::type>::type
cdr(T&& t)
{
return cdr_impl(std::forward<T>(t),
std::make_index_sequence<std::tuple_size<T>::value>{});
}

Это создает целочисленную последовательность [0,1,2,...,N) где N является tuple_size<T>::value, а затем создает новый кортеж с make_tuple(get<I>(t)...) за I в [1,2,...,N)

Тестирование это:

using tuple1 = std::tuple<int, short, double>;
using tuple2 = std::tuple<short, double>;
using transformed = decltype(cdr(std::declval<tuple1>()));
static_assert(std::is_same<transformed, tuple2>::value, "");
static_assert(std::is_same<tuple_cdr<tuple1>::type, tuple2>::value, "");#include <iostream>

int main()
{
auto t = cdr(std::make_tuple(nullptr, "hello", "world"));
std::cout << std::get<0>(t) << ", " << std::get<1>(t) << '\n';
}

Моя эталонная реализация для предложения в https://gitlab.com/redistd/integer_seq/blob/master/integer_seq.h

8

Я придумал решение, очень похожее на предложенное @Andy, но оно пытается быть более общим, работая непосредственно с пакетом параметров (используя фиктивную оболочку), а не над std::tuple, Таким образом, операция может быть применена и к другим шаблонам переменных, не только к кортежам:

#include <type_traits>
#include <tuple>

template <typename... Args> struct pack {};

template <template <typename...> class T, typename Pack>
struct unpack;

template <template <typename...> class T, typename... Args>
struct unpack<T, pack<Args...>>
{
typedef T<Args...> type;
};

template <typename T, typename Pack>
struct prepend;

template <typename T, typename... Args>
struct prepend<T, pack<Args...>>
{
typedef pack<T, Args...> type;
};

template <std::size_t N, typename... Args>
struct remove_nth_type;

template <std::size_t N, typename T, typename... Ts>
struct remove_nth_type<N, T, Ts...>
: prepend<T, typename remove_nth_type<N-1, Ts...>::type>
{};

template <typename T, typename... Ts>
struct remove_nth_type<0, T, Ts...>
{
typedef pack<Ts...> type;
};

template <typename T, int N>
struct remove_nth;

template <template <typename...> class T, int N, typename... Args>
struct remove_nth<T<Args...>, N>
{
typedef typename
unpack<
T, typename
remove_nth_type<N, Args...>::type
>::type type;
};

template <typename... Args>
struct my_variadic_template
{
};

int main()
{
typedef std::tuple<int, bool, double> my_tuple;
typedef remove_nth<my_tuple, 1>::type my_tuple_wo_2nd_type;

static_assert(
is_same<my_tuple_wo_2nd_type, tuple<int, double>>::value,
"Error!");

typedef my_variadic_template<int, double> vt;
typedef remove_nth<vt, 0>::type vt_wo_1st_type;

static_assert(
is_same<vt_wo_1st_type, my_variadic_template<double>>::value,
"Error!");
}

pack является вспомогательной структурой, единственной целью которой является сохранение пакета параметров шаблона. unpack затем можно использовать для распаковки параметров в произвольный шаблон класса (спасибо @BenVoigt за этот трюк). prepend просто добавляет тип к пачке.

remove_nth_type использует частичную специализацию шаблона для удаления n-го типа из пакета параметров, сохраняя результат в pack, В заключение, remove_nth берет специализацию произвольного шаблона класса, удаляет n-й тип из его параметров шаблона и возвращает новую специализацию.

3

Это слишком спроектированный бит template метапрограммирование для этой задачи. Он включает в себя возможность произвольного переупорядочения / дублирования / удаления на типах tuple через фильтр template:

#include <utility>
#include <type_traits>

template<typename... Ts> struct pack {};

template<std::size_t index, typename Pack, typename=void> struct nth_type;

template<typename T0, typename... Ts>
struct nth_type<0, pack<T0, Ts...>, void> { typedef T0 type; };

template<std::size_t index, typename T0, typename... Ts>
struct nth_type<index, pack<T0, Ts...>, typename std::enable_if<(index>0)>::type>:
nth_type<index-1, pack<Ts...>>
{};

template<std::size_t... s> struct seq {};

template<std::size_t n, std::size_t... s>
struct make_seq:make_seq<n-1, n-1, s...> {};

template<std::size_t... s>
struct make_seq<0,s...> {
typedef seq<s...> type;
};

template<typename T, typename Pack> struct conc_pack { typedef pack<T> type; };
template<typename T, typename... Ts> struct conc_pack<T, pack<Ts...>> { typedef pack<T, Ts...> type; };

template<std::size_t n, typename Seq> struct append;
template<std::size_t n, std::size_t... s>
struct append<n, seq<s...>> {
typedef seq<n, s...> type;
};
template<typename S0, typename S1> struct conc;
template<std::size_t... s0, std::size_t... s1>
struct conc<seq<s0...>, seq<s1...>>
{
typedef seq<s0..., s1...> type;
};

template<typename T, typename=void> struct value_exists:std::false_type {};

template<typename T> struct value_exists<T,
typename std::enable_if< std::is_same<decltype(T::value),decltype(T::value)>::value >::type
>:std::true_type {};

template<typename T, typename=void> struct result_exists:std::false_type {};
template<typename T> struct result_exists<T,
typename std::enable_if< std::is_same<typename T::result,typename T::result>::value >::type
>:std::true_type {};

template<template<std::size_t>class filter, typename Seq, typename=void>
struct filter_seq { typedef seq<> type; };

template<template<std::size_t>class filter, std::size_t s0, std::size_t... s>
struct filter_seq<filter, seq<s0, s...>, typename std::enable_if<value_exists<filter<s0>>::value>::type>
: append< filter<s0>::value, typename filter_seq<filter, seq<s...>>::type >
{};

template<template<std::size_t>class filter, std::size_t s0, std::size_t... s>
struct filter_seq<filter, seq<s0, s...>, typename std::enable_if<!value_exists<filter<s0>>::value && result_exists<filter<s0>>::value>::type>
: conc< typename filter<s0>::result, typename filter_seq<filter, seq<s...>>::type >
{};

template<template<std::size_t>class filter, std::size_t s0, std::size_t... s>
struct filter_seq<filter, seq<s0, s...>, typename std::enable_if<!value_exists<filter<s0>>::value && !result_exists<filter<s0>>::value>::type>
: filter_seq<filter, seq<s...>>
{};

template<typename Seq, typename Pack>
struct remap_pack {
typedef pack<> type;
};

template<std::size_t s0, std::size_t... s, typename Pack>
struct remap_pack< seq<s0, s...>, Pack >
{
typedef typename conc_pack< typename nth_type<s0, Pack>::type, typename remap_pack< seq<s...>, Pack >::type >::type type;
};

template<typename Pack>
struct get_indexes { typedef seq<> type; };

template<typename... Ts>
struct get_indexes<pack<Ts...>> {
typedef typename make_seq< sizeof...(Ts) >::type type;
};

template<std::size_t n>
struct filter_zero_out { enum{ value = n }; };

template<>
struct filter_zero_out<0> {};

template<std::size_t n>
struct filter_zero_out_b { typedef seq<n> result; };

template<>
struct filter_zero_out_b<0> { typedef seq<> result; };

#include <iostream>

int main() {
typedef pack< int, double, char > pack1;
typedef pack< double, char > pack2;

typedef filter_seq< filter_zero_out, typename get_indexes<pack1>::type >::type reindex;
typedef filter_seq< filter_zero_out_b, typename get_indexes<pack1>::type >::type reindex_b;

typedef typename remap_pack< reindex, pack1 >::type pack2_clone;
typedef typename remap_pack< reindex_b, pack1 >::type pack2_clone_b;

std::cout << std::is_same< pack2, pack2_clone >::value << "\n";
std::cout << std::is_same< pack2, pack2_clone_b >::value << "\n";
}

Здесь у нас есть тип pack который содержит произвольный список типов. Смотрите аккуратный ответ @LucTouraille о том, как перемещаться между tuple а также pack,

seq содержит последовательность индексов. remap_pack занимает seq и packи создает в результате pack схватив n-й элемент оригинала pack,

filter_seq занимает template<size_t> функтор и seqи использует функтор для фильтрации элементов seq, Функтор может вернуть либо ::value типа size_t или ::result типа seq<...> или ни того, ни другого, допуская функторы один-к-одному или один-ко-многим.

Несколько других вспомогательных функций, таких как conc, append, conc_pack, get_indexes, make_seq, nth_type вокруг вещи.

Я проверил это с filter_zero_out который является ::value на основе фильтра, который удаляет 0, и filter_zero_out_b который является ::result основанный фильтр, который также удаляет 0.

1
По вопросам рекламы [email protected]