2011-12-20 8 views
20

tengo una tupla de tamaño desconocido (que es la plantilla parametr del método)obtener parte de std :: tupla

¿Es manera de conseguir parte de ella (lo que necesito tirar primer elemento de la misma)

Por ejemplo, tengo tuple<int,int,int>(7,12,42). Quiero tuple<int,int>(12,42) aquí

+1

¿Tiene usted un verdadero soporte de plantilla variadic ('plantilla ')? Además, ¿desea copiar los valores o desea una * vista * de ellos, como en, referencias al original? – Xeo

+0

@Xeo: Quiero referencias mejor, pero en realidad no es materia. – RiaD

+0

@Xeo: ahora no uso plantillas variadic. Pero uso C++ 0x en g ++ 4.6 y creo que son compatibles. – RiaD

Respuesta

15

con Ayuda de una lista de enteros en tiempo de compilación:

#include <cstdlib> 

template <size_t... n> 
struct ct_integers_list { 
    template <size_t m> 
    struct push_back 
    { 
     typedef ct_integers_list<n..., m> type; 
    }; 
}; 

template <size_t max> 
struct ct_iota_1 
{ 
    typedef typename ct_iota_1<max-1>::type::template push_back<max>::type type; 
}; 

template <> 
struct ct_iota_1<0> 
{ 
    typedef ct_integers_list<> type; 
}; 

Podríamos construir la cola simplemente por expansión de parámetro-pack:

#include <tuple> 

template <size_t... indices, typename Tuple> 
auto tuple_subset(const Tuple& tpl, ct_integers_list<indices...>) 
    -> decltype(std::make_tuple(std::get<indices>(tpl)...)) 
{ 
    return std::make_tuple(std::get<indices>(tpl)...); 
    // this means: 
    // make_tuple(get<indices[0]>(tpl), get<indices[1]>(tpl), ...) 
} 

template <typename Head, typename... Tail> 
std::tuple<Tail...> tuple_tail(const std::tuple<Head, Tail...>& tpl) 
{ 
    return tuple_subset(tpl, typename ct_iota_1<sizeof...(Tail)>::type()); 
    // this means: 
    // tuple_subset<1, 2, 3, ..., sizeof...(Tail)-1>(tpl, ..) 
} 

Uso:

#include <cstdio> 

int main() 
{ 
    auto a = std::make_tuple(1, "hello", 7.9); 
    auto b = tuple_tail(a); 

    const char* s = nullptr; 
    double d = 0.0; 
    std::tie(s, d) = b; 
    printf("%s %g\n", s, d); 
    // prints: hello 7.9 

    return 0; 
} 

(En Ideone: http://ideone.com/Tzv7v; el código funciona en g ++ 4.5 a 4.7 y clang ++ 3.0)

+0

Estaba buscando algo "que venga de la fábrica". Y creo que el preguntador también estaba pidiendo tal cosa. – sergiol

+1

Tenga en cuenta que puede usar 'std :: index_sequence' en lugar de' ct_integers_list' en C++ 14. – LogicStuff

9

Puede haber una manera más fácil, pero esto es un comienzo. La plantilla de función "cola" devuelve una tupla copiada con todos los valores del original excepto el primero. Esto compila con GCC 4.6.2 en C++ 0x-mode.

template<size_t I> 
struct assign { 
    template<class ResultTuple, class SrcTuple> 
    static void x(ResultTuple& t, const SrcTuple& tup) { 
    std::get<I - 1>(t) = std::get<I>(tup); 
    assign<I - 1>::x(t, tup); 
    } 
}; 

template<> 
struct assign<1> { 
    template<class ResultTuple, class SrcTuple> 
    static void x(ResultTuple& t, const SrcTuple& tup) { 
    std::get<0>(t) = std::get<1>(tup); 
    } 
}; 


template<class Tup> struct tail_helper; 

template<class Head, class... Tail> 
struct tail_helper<std::tuple<Head, Tail...>> { 
    typedef typename std::tuple<Tail...> type; 
    static type tail(const std::tuple<Head, Tail...>& tup) { 
    type t; 
    assign<std::tuple_size<type>::value>::x(t, tup); 
    return t; 
    } 
}; 

template<class Tup> 
typename tail_helper<Tup>::type tail(const Tup& tup) { 
    return tail_helper<Tup>::tail(tup); 
} 
+0

Solo quiero señalar que desde 'template struct tuple_trunc {};' nunca se crea una instancia, se puede escribir como: 'template struct tuple_trunc; 'que conducirá a error del compilador si alguien accidentalmente hace esto. –

+0

@veso - No veo ningún "tuple_trunc" en esta respuesta. –

+0

Sin embargo, su implementación requiere un tipo constructivo predeterminado. – Jarod42

6

Hice algunas modificaciones para Adam's code que quitarse los primeros N argumentos de la tupla, así como crear una nueva tupla con sólo los últimos tipos N ... Aquí está el código completo (nota: si alguien decide hacer +1 en mi respuesta, también, por favor 1 respuesta de Adán ya que es lo que este código se basa en, y no desean tomar ningún crédito fuera de su contribución):

//create a struct that allows us to create a new tupe-type with the first 
//N types truncated from the front 

template<size_t N, typename Tuple_Type> 
struct tuple_trunc {}; 

template<size_t N, typename Head, typename... Tail> 
struct tuple_trunc<N, std::tuple<Head, Tail...>> 
{ 
    typedef typename tuple_trunc<N-1, std::tuple<Tail...>>::type type; 
}; 

template<typename Head, typename... Tail> 
struct tuple_trunc<0, std::tuple<Head, Tail...>> 
{ 
    typedef std::tuple<Head, Tail...> type; 
}; 

/*-------Begin Adam's Code----------- 

Note the code has been slightly modified ... I didn't see the need for the extra 
variadic templates in the "assign" structure. Hopefully this doesn't break something 
I didn't forsee 

*/ 

template<size_t N, size_t I> 
struct assign 
{ 
    template<class ResultTuple, class SrcTuple> 
    static void x(ResultTuple& t, const SrcTuple& tup) 
    { 
     std::get<I - N>(t) = std::get<I>(tup); 
     assign<N, I - 1>::x(t, tup); //this offsets the assignment index by N 
    } 
}; 

template<size_t N> 
struct assign<N, 1> 
{ 
    template<class ResultTuple, class SrcTuple> 
    static void x(ResultTuple& t, const SrcTuple& tup) 
    { 
     std::get<0>(t) = std::get<1>(tup); 
    } 
}; 


template<size_t TruncSize, class Tup> struct th2; 

//modifications to this class change "type" to the new truncated tuple type 
//as well as modifying the template arguments to assign 

template<size_t TruncSize, class Head, class... Tail> 
struct th2<TruncSize, std::tuple<Head, Tail...>> 
{ 
    typedef typename tuple_trunc<TruncSize, std::tuple<Head, Tail...>>::type type; 

    static type tail(const std::tuple<Head, Tail...>& tup) 
    { 
     type t; 
     assign<TruncSize, std::tuple_size<type>::value>::x(t, tup); 
     return t; 
    } 
}; 

template<size_t TruncSize, class Tup> 
typename th2<TruncSize, Tup>::type tail(const Tup& tup) 
{ 
    return th2<TruncSize, Tup>::tail(tup); 
} 

//a small example 
int main() 
{ 
    std::tuple<double, double, int, double> test(1, 2, 3, 4); 
    tuple_trunc<2, std::tuple<double, double, int, double>>::type c = tail<2>(test); 
    return 0; 
} 
+0

Parece que tienes razón sobre los varidics allí, debe haber sido sobrante de un enfoque anterior. –

2

No lo use!

  • Este es [muy probablemente] comportamiento no especificado. Podría dejar de funcionar en cualquier momento.
  • Además, existe la posibilidad de problemas de relleno (es decir, puede funcionar para int, pero puede fallar para su tipo!).

Consulte los comentarios para la discusión. Dejo esta respuesta solo para referencia.


aún más simple:

tuple<int,int,int> origin{7,12,42}; 
tuple<int, int> &tail1 = (tuple<int, int>&)origin; 
tuple<int> &tail2 = (tuple<int>&)origin; 
cout << "tail1: {" << get<0>(tail1) << ", " << get<1>(tail1) << "}" << endl; 
cout << "tail2: {" << get<0>(tail2) << "}" << endl; 

que tengo:

tail1: {12, 42} 
tail2: {42} 

No estoy seguro de que esto no es un comportamiento no especificado. Que funciona para mí: Fedora 20 y

❯ clang --version 
clang version 3.3 (tags/RELEASE_33/final) 
Target: x86_64-redhat-linux-gnu 
Thread model: posix 
❯ gcc --version 
gcc (GCC) 4.8.2 20131212 (Red Hat 4.8.2-7) 
Copyright (C) 2013 Free Software Foundation, Inc. 
This is free software; see the source for copying conditions. There is NO 
warranty; not even for MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. 

Referencias: article on voidnish.wordpress.com/.

+0

Me encantan los votos atrasados ​​sin explicación ... – kgadek

+0

esta es una hermosa respuesta. ¿es portátil? –

+0

Francamente no tengo idea. Viendo mi respuesta ahora y pensando en eso, supongo que desafortunadamente podría no ser portátil. – kgadek

3

A tupla operación rebanada (que también funciona para std::array y std::pair) se puede definir como esto (C++ 14 required):

namespace detail 
{ 
    template <std::size_t Ofst, class Tuple, std::size_t... I> 
    constexpr auto slice_impl(Tuple&& t, std::index_sequence<I...>) 
    { 
     return std::forward_as_tuple(
      std::get<I + Ofst>(std::forward<Tuple>(t))...); 
    } 
} 

template <std::size_t I1, std::size_t I2, class Cont> 
constexpr auto tuple_slice(Cont&& t) 
{ 
    static_assert(I2 >= I1, "invalid slice"); 
    static_assert(std::tuple_size<std::decay_t<Cont>>::value >= I2, 
     "slice index out of bounds"); 

    return detail::slice_impl<I1>(std::forward<Cont>(t), 
     std::make_index_sequence<I2 - I1>{}); 
} 

Y un subconjunto arbitrario de una tupla t puede obtenerse de este modo:

tuple_slice<I1, I2>(t); 

Dónde [I1, I2) es el gama exclusiva del subconjunto y el valor de retorno es un tupl e de referencias o valores dependiendo de si la tupla de entrada es un lvalue o un valor r respectivamente (se puede encontrar una explicación detallada en mi blog).

4

con C++ 17, puede utilizar std::apply:

template <typename Head, typename... Tail> 
std::tuple<Tail...> tuple_tail(const std::tuple<Head, Tail...>& t) 
{ 
    return apply([](auto head, auto... tail) { 
     return std::make_tuple(tail...)}; 
    }, t); 
}