Как разбить кортеж?
Учитывая
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;
}
};
}
Ответы
Ответ 1
Я искал то же самое и придумал это довольно прямое решение С++ 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.
Ответ 2
Использование "индексного кортежа" для распаковки кортежа без рекурсии:
#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
, которые являются важными утилитами IMHO для работы с кортежами и аналогичными шаблонами вариационного класса. (Аналогичная утилита была стандартизирована как std::index_sequence
в С++ 14, см. index_seq.h для самостоятельной реализации С++ 11).
В качестве альтернативы, не копируя версию, используя std::tie
, чтобы получить кортеж ссылок на хвост, вместо создания копий каждого элемента:
#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...>()); }
Ответ 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; }
Ответ 4
Crazy Eddie нашел способ распаковать кортеж, который отвечает на вопрос. Однако для конкретного вопроса, который вы задали (например, хеширование кортежей), почему бы не избежать всех копий кортежей и вместо этого использовать рекурсию шаблона для хеширования каждого элемента в свою очередь?
#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 являются коммутативными, поэтому это не имеет значения.
Ответ 5
Что-то вроде этого:
#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>
Ответ 6
Использование ответа kgadek на получить часть 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;
}