Вопрос по c++, c++11 – Как разбить кортеж?

16

Учитывая

   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   ответов
3

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

#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 являются коммутативными, так что это не имеет значения.

4

Используя & 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...>()); }
Вы можете сопоставить пакет индексов сredi::index_tuple<0, I...> а такжеI начнется с индекса1 то есть только те индексы, которые нас интересуют.
@ Crazy Eddie, да, но это утилита, которую вы пишете один раз (или копируете) и используете повторно, вместо того, чтобы заново реализовывать рекурсивное решение каждый раз, когда вы что-то делаете с кортежами.
@Luc Danton, хорошая идея, но вам нужны и другие изменения, или пакет параметров у меня не будет такого же размера, как U
Тип, основанный наindex_tuple будет в C ++ 14 :)
Вы по-прежнему используете рекурсию, только в утилитах index_tuple.h, которые вы используете.
0

Используя ответ кгадека наполучить часть 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;
}
Это UB какstd::tuple<T, Ts...> а такжеstd::tuple<Ts...> являются несвязанными типами (даже если действительно некоторые старые реализации использовали наследование, но это деталь реализации, поэтому не переносимая).
3

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

#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; }
11

Я искал то же самое и придумал это довольно простое решение 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 элементов.

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>
Мой тест: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
Извините, были некоторые ошибки. Исправлено сейчас.

Похожие вопросы