Как разбить кортеж?

Учитывая

   template<typename First, typename... Tail>
   struct something
   {
       std::tuple<First, Tail...> t;
   };

Как я могу получитьstd::tuple<Tail...> который содержит все элементы изt кроме первого?

Я думаю, что это интересный вопрос в целом, но вот моя мотивация для контекста:

Я хотел бы реализовать хэш для кортежей. я использовалэтот ответ в качестве основы. Я обнаружил, что в нем произошла ошибка, а именно не звонилoperator() хеш-объекта со значением:

return left() ^ right();

Должно быть:

return left(std::get<0>(e)) ^ right(???);

??? будут оставшимися элементами кортежа для продолжения рекурсивного создания шаблона. Вот полный код, включая завершающую часть:

#include <functional>
#include <utility>

namespace std
{

template<typename First, typename... Tail>
struct hash<std::tuple<First, Tail...>>
{
    typedef size_t result_type;
    typedef std::tuple<First, Tail...> argument_type;

    result_type operator()(argument_type const& e) const
    {
        std::hash<First> left;
        std::hash<std::tuple<Tail...>> right;
        return left(std::get<0>(e)) ^ right(???);
    }
};

template<>
struct hash<std::tuple<>>
{
    typedef size_t result_type;
    typedef std::tuple<> argument_type;

    result_type operator()(argument_type const& e) const
    {
        return 1;
    }
};

}

Ответы на вопрос(6)

Сумасшедший Эдди нашел способ распаковать кортеж, который действительно отвечает на вопрос. Однако для конкретного вопроса, который вы задали (например, хеширование кортежей), почему бы не избежать всех копий кортежей и вместо этого использовать рекурсию шаблона для хеширования каждого элемента по очереди?

#include <utility>
#include <iostream>

template< typename T >
size_t left( T const & ) {
  return 1;
}

template< int N, typename Head, typename... Tail >
struct _hash {
  typedef size_t result_type;
  typedef std::tuple< Head, Tail... > argument_type;

  result_type operator ()( argument_type const &e ) const {
    return left(std::get<N>(e)) ^ _hash<N-1, Head, Tail... >()(e);
  }
}; // end struct _hash

template< typename Head, typename... Tail >
struct _hash< 0, Head, Tail... > {
  typedef size_t result_type;
  typedef std::tuple< Head, Tail... > argument_type;

  result_type operator ()( argument_type const &e ) const {
    return left(std::get<0>(e));
  }
}; // end struct _hash< 0 >

template< typename Head, typename... Tail >
size_t hash( std::tuple< Head, Tail... > const &e ) {
  return _hash< sizeof...(Tail), Head, Tail... >()( e );
}

int main( ) {
  std::tuple< int > l_tuple( 5 );
  std::cout << hash( l_tuple ) << std::endl;
}

Это делает хеширование в обратном порядке, но xors являются коммутативными, так что это не имеет значения.

Используя & quot; индексный кортеж & quot; распаковать кортеж без рекурсии:

#include <redi/index_tuple.h>

template<typename T, typename... U, unsigned... I>
  inline std::tuple<U...>
  cdr_impl(const std::tuple<T, U...>& t, redi::index_tuple<I...>)
  { return std::tuple<U...>{ std::get<I+1>(t)... }; }

template<typename T, typename... U>
  inline std::tuple<U...>
  cdr(const std::tuple<T, U...>& t)
  { return cdr_impl(t, redi::to_index_tuple<U...>()); }

Увидетьhttps://gitlab.com/redistd/redistd/blob/master/include/redi/index_tuple.h заmake_index_tuple а такжеindex_tuple которые являются ИМХО важными утилитами для работы с кортежами и подобными шаблонами классов с переменным числом аргументов. (Подобная утилита была стандартизирована какstd::index_sequence в C ++ 14 см.index_seq.h для отдельной реализации C ++ 11).

Альтернативно, не копирующая версия, использующаяstd::tie получить кортежreferences в хвост, вместо того, чтобы делать копии каждого элемента:

#include <redi/index_tuple.h>

template<typename T, typename... U, unsigned... I>
  inline std::tuple<const U&...>
  cdr_impl(const std::tuple<T, U...>& t, redi::index_tuple<I...>)
  { return std::tie( std::get<I+1>(t)... ); }

template<typename T, typename... U>
  inline std::tuple<const U&...>
  cdr(const std::tuple<T, U...>& t)
  { return cdr_impl(t, redi::to_index_tuple<U...>()); }
 21 апр. 2013 г., 22:24
Тип, основанный наindex_tuple будет в C ++ 14 :)
 17 мая 2012 г., 02:07
Вы можете сопоставить пакет индексов сredi::index_tuple<0, I...> а такжеI начнется с индекса1 то есть только те индексы, которые нас интересуют.
 17 мая 2012 г., 03:13
@ Crazy Eddie, да, но это утилита, которую вы пишете один раз (или копируете) и используете повторно, вместо того, чтобы заново реализовывать рекурсивное решение каждый раз, когда вы что-то делаете с кортежами.
 17 мая 2012 г., 03:13
@Luc Danton, хорошая идея, но вам нужны и другие изменения, или пакет параметров у меня не будет такого же размера, как U
 17 мая 2012 г., 02:26
Вы по-прежнему используете рекурсию, только в утилитах index_tuple.h, которые вы используете.

Используя ответ кгадека наполучить часть std :: tuple и тестовый код Андре Бергнера. Это красиво и просто, но я не знаю, портативна ли она.

// works using gcc 4.6.3
// g++ -std=c++0x -W -Wall -g main.cc -o main
#include <iostream>
#include <tuple>

template < typename T , typename... Ts >
const T& head(std::tuple<T,Ts...> t)
{
   return  std::get<0>(t);
}

template <typename T, typename... Ts>
const std::tuple<Ts...>& tail(const std::tuple<T, Ts...>& t)
{
   return (const std::tuple<Ts...>&)t;
}

int main()
{
   auto t = std::make_tuple( 2, 3.14 , 'c' );
   std::cout << head(t) << std::endl;
   std::cout << std::get<0>( tail(t) ) << std::endl;
   std::cout << std::get<1>( tail(t) ) << std::endl;
}
 27 нояб. 2018 г., 17:06
Это UB какstd::tuple<T, Ts...> а такжеstd::tuple<Ts...> являются несвязанными типами (даже если действительно некоторые старые реализации использовали наследование, но это деталь реализации, поэтому не переносимая).

Это то, что я мог выбить с первой попытки. Вероятно, что-то лучше:

#include <tuple>

template < typename Target, typename Tuple, int N, bool end >
struct builder
{
    template < typename ... Args >
    static Target create(Tuple const& t, Args && ... args)
    {
        return builder<Target,Tuple, N+1, std::tuple_size<Tuple>::value == N+1>::create(t, std::forward<Args>(args)..., std::get<N>(t));
    }
};

template < typename Target, typename Tuple, int N >
struct builder<Target,Tuple,N,true>
{
    template < typename ... Args >
    static Target create(Tuple const& t, Args && ... args) { return Target(std::forward<Args>(args)...); }
};

template < typename Head, typename ... Tail >
std::tuple<Tail...> cdr(std::tuple<Head,Tail...> const& tpl)
{
    return builder<std::tuple<Tail...>, std::tuple<Head,Tail...>, 1, std::tuple_size<std::tuple<Head,Tail...>>::value == 1>::create(tpl);
}

#include <iostream>
int main() {
    std::tuple<int,char,double> t1(42,'e',16.7);
    std::tuple<char,double> t2 = cdr(t1);

    std::cout << std::get<0>(t2) << std::endl;
}

Следует отметить, что если вы используете собственный тип вместо std :: tuple, вам, вероятно, будет гораздо лучше. Причина, по которой это так сложно, состоит в том, что стандарт, по-видимому, не определяет, как работает этот кортеж, поскольку он не учитывает, что он наследуется от самого себя. Буст-версия использует минусы, через которые вы можете копаться. Вот что-то, что могло бы более соответствовать тому, что вы хотите, и сделало бы выполнение всего вышеперечисленного таким простым, как приведение:

template < typename ... Args > struct my_tuple;

template < typename Head, typename ... Tail >
struct my_tuple<Head,Tail...> : my_tuple<Tail...>
{
    Head val;
    template < typename T, typename ... Args >
    my_tuple(T && t, Args && ... args) 
        : my_tuple<Tail...>(std::forward<Args>(args)...)
        , val(std::forward<T>(t)) 
    {}
};

template < >
struct my_tuple <>
{
};

Это не проверено, но это должно проиллюстрировать достаточно точку, чтобы играть, пока он не работает. Теперь, чтобы получить объект типа «хвост» вы просто делаете:

template < typename Head, typename ... Tail >
my_tuple<Tail...> cdr(my_tuple<Head,Tail...> const& mtpl) { return mtpl; }

Я искал то же самое и придумал это довольно простое решение C ++ 14:

#include <iostream>
#include <tuple>
#include <utility>

template < typename T , typename... Ts >
auto head( std::tuple<T,Ts...> t )
{
   return  std::get<0>(t);
}

template < std::size_t... Ns , typename... Ts >
auto tail_impl( std::index_sequence<Ns...> , std::tuple<Ts...> t )
{
   return  std::make_tuple( std::get<Ns+1u>(t)... );
}

template < typename... Ts >
auto tail( std::tuple<Ts...> t )
{
   return  tail_impl( std::make_index_sequence<sizeof...(Ts) - 1u>() , t );
}

int main()
{
   auto t = std::make_tuple( 2, 3.14 , 'c' );
   std::cout << head(t) << std::endl;
   std::cout << std::get<0>( tail(t) ) << std::endl;
   std::cout << std::get<1>( tail(t) ) << std::endl;
}

Таким образом, head (.) Возвращает первый элемент кортежа, а tail (.) Возвращает новый кортеж, содержащий только последние N-1 элементов.

Что-то вроде этого:

#include <tuple>

template <bool, typename T, unsigned int ...N> struct tail_impl;

template <typename T, typename ...Args, unsigned int ...N>
struct tail_impl<false, std::tuple<T, Args...>, N...>
{
    static std::tuple<Args...> go(std::tuple<T, Args...> const & x)
    {
        return tail_impl<sizeof...(N) + 1 == sizeof...(Args), std::tuple<T, Args...>, N..., sizeof...(N)>::go(x);
    }
};

template <typename T, typename ...Args, unsigned int ...N>
struct tail_impl<true, std::tuple<T, Args...>, N...>
{
    static std::tuple<Args...> go(std::tuple<T, Args...> const & x)
    {
        return std::tuple<Args...>(std::get<N>(x)...);
    }
};

template <typename T, typename ...Args>
std::tuple<Args...> tail(std::tuple<T, Args...> const & x)
{
    return tail_impl<sizeof...(Args) == 1, std::tuple<T, Args...>, 0>::go(x);
}

Тестовое задание:

#include <demangle.hpp>
#include <iostream>

typedef std::tuple<int, char, bool> TType;

int main()
{
    std::cout << demangle<TType>() << std::endl;
    std::cout << demangle<decltype(tail(std::declval<TType>()))>() << std::endl;
}

Печать:

std::tuple<int, char, bool>
std::tuple<char, bool>
 17 мая 2012 г., 00:02
Мой тест:int main() { std::tuple<int,char,double> t1(42,'e',16.7); std::tuple<char,double> t2 = tail(t1); std::cout << std::get<0>(t2) << std::endl; } не может скомпилировать:error: cannot call member function 'std::tuple<Args ...> tail_impl<false, std::tuple<T, Args ...>, N ...>::go(const std::tuple<T, Args ...>&) [with T = int, Args = {char, double}, unsigned int ...N = {0u}]' without object
 17 мая 2012 г., 07:29
Извините, были некоторые ошибки. Исправлено сейчас.

Ваш ответ на вопрос