top today popular new most hashtags cool c++ c++11 variadic-templates

c++ - today - Los paquetes de parámetros de plantilla acceden al tipo Nth y al elemento Nth



trending hashtags instagram (5)

Para obtener el N-ésimo elemento de un paquete, puede escribir:

Opción 1

Usando tuple_element para obtener el tipo de retorno para el elemento Nth:

template<size_t index, typename T, typename... Ts> inline constexpr typename enable_if<index==0, T>::type get(T&& t, Ts&&... ts) { return t; } template<size_t index, typename T, typename... Ts> inline constexpr typename enable_if<(index > 0) && index <= sizeof...(Ts), typename tuple_element<index, tuple<T, Ts...>>::type>::type get(T&& t, Ts&&... ts) { return get<index-1>(std::forward<Ts>(ts)...); } // below is optional - just for getting a more readable compilation error // in case calling get with a bad index inline template<long long index, typename... Ts> constexpr bool index_ok() { return index >= 0 && index < sizeof...(Ts); } template<long long index, typename T, typename... Ts> inline constexpr typename enable_if<!index_ok<index, T, Ts...>(), T>::type get(T&& t, Ts&&... ts) { static_assert(index_ok<index, T, Ts...>(), "bad index in call to get, smaller than zero or above pack size"); return t; }

opcion 2

Sin usar la tupla , confíe en el tipo de devolución automática y específicamente en decltype C ++ 14 (automático) y en el uso de enable_if como parámetro de plantilla y no como tipo de devolución:

template<size_t index, typename T, typename... Ts, typename enable_if<index==0>::type* = nullptr> inline constexpr decltype(auto) get(T&& t, Ts&&... ts) { return std::forward<T>(t); } template<size_t index, typename T, typename... Ts, typename enable_if<(index > 0 && index <= sizeof...(Ts))>::type* = nullptr> inline constexpr decltype(auto) get(T&& t, Ts&&... ts) { return get<index-1>(std::forward<Ts>(ts)...); } template<long long index, typename... Ts> inline constexpr bool index_ok() { return index >= 0 && index < (long long)sizeof...(Ts); } // block (compilation error) the call to get with bad index, // providing a readable compilation error template<long long index, typename T, typename... Ts, typename enable_if<(!index_ok<index, T, Ts...>())>::type* = nullptr> inline constexpr decltype(auto) get(T&& t, Ts&&... ts) { static_assert(index_ok<index, T, Ts...>(), "bad index in call to get, smaller than zero or above pack size"); return std::forward<T>(t); // need to return something... // we hope to fail on the static_assert above }

Ejemplo de uso:

template<size_t index, typename... Ts> void resetElementN(Ts&&... ts) { get<index>(std::forward<Ts>(ts)...) = {}; // assuming element N has an empty ctor } int main() { int i = 0; string s = "hello"; get<0>(i,2,"hello","hello"s, ''a'') += get<0>(2); get<1>(1,i,"hello",4) += get<1>(1, 2); get<3>(1,2,"hello",i) += get<2>(0, 1, 2); get<2>(1,2,s,4) = get<2>(0, 1, "hi"); cout << i << '' '' << s << endl; resetElementN<1>(0, i, 2); resetElementN<0>(s, 1, 2); cout << i << '' '' << s << endl; // not ok - and do not compile // get<0>(1,i,"hello","hello"s) = 5; // get<1>(1,i*2,"hello") = 5; // get<2>(1,i*2,"hello")[4] = ''!''; // resetElementN<1>(s, 1, 2); // ok const int j = 2; cout << get<0>(j,i,3,4) << endl; // not ok - and do not compile // get<0>(j,i,3,4) = 5; // not ok - and do not compile // with a readable compilation error // cout << get<-1>("one", 2, ''3'') << endl; // cout << get<3>("one", 2, ''3'') << endl; }

Código
Opción 1: http://coliru.stacked-crooked.com/a/60ad3d860aa94453
Opción 2: http://coliru.stacked-crooked.com/a/09f6e8e155612f8b

El siguiente artículo es la primera propuesta que encontré para los paquetes de parámetros de plantilla.

http://www.open-std.org/jtc1/sc22/wg21/docs/papers/2004/n1603.pdf

En la página 16, se trata de introducir dos nuevos operadores [] y <> para acceder a los elementos del paquete de parámetros y los tipos de paquetes de parámetros.

The suggested syntax for such an operator involves two new operators: .[] to access values and .<> to access types. For instance:

template<int N, typename Tuple> struct tuple_element; template<int N, ... Elements> struct tuple_element<tuple<Elements...> > { typedef Elements.<N> type; }; template<int N, ... Elements> Elements.<N>& get(tuple<Elements...>& t) { return t.[N]; } template<int N, ... Elements> const Elements.<N>& get(const tuple<Elements...>& t) { return t.[N]; }

Entonces, ¿dónde están estos operadores? Si no hay ninguno, ¿cuál es su reemplazo?


C ++ 11 no tiene operadores correspondientes, por lo que se proponen. Con C ++ 11 necesitará extraer la información correspondiente por sí mismo o usar una clase que ya realice la operación necesaria. El enfoque más fácil es probablemente usar std::tuple<T...> que ya implementa la lógica correspondiente.

Si se pregunta cómo std::tuple<T...> implementa actualmente estas operaciones: es básicamente un ejercicio de programación funcional que utiliza una notación de programación funcional bastante mala. Una vez que sepa cómo obtener el n -ésimo tipo de la secuencia, obtener el n ésimo elemento utilizando la herencia de las clases base parametrizadas en el índice y el tipo es bastante trivial. Implementar algo como tuple_element<N, T...> podría tener este aspecto:

template <int N, typename... T> struct tuple_element; template <typename T0, typename... T> struct tuple_element<0, T0, T...> { typedef T0 type; }; template <int N, typename T0, typename... T> struct tuple_element<N, T0, T...> { typedef typename tuple_element<N-1, T...>::type type; };

El bit más desafiante en la implementación de algo como std::tuple<T...> es evocar una lista de índices, por lo que tienes una lista paralela de tipos y enteros que luego se pueden expandir, por ejemplo, para una lista de clases base usar algo como (el aspecto exacto de los detalles internos será diferente, pero la idea básica de tener paquetes de parámetros paralelos para los tipos y sus índices estará de alguna manera allí):

template <typename... T, int... I> class tuple_base<tuple_types<T...>, tuple_indices<I...>>: public tuple_field<T, I>... { };


Otros ya han respondido que se puede hacer a través de std::tuple . Si desea acceder al tipo Nth de un paquete de parámetros, puede encontrar la siguiente metafunción a mano:

template<int N, typename... Ts> using NthTypeOf = typename std::tuple_element<N, std::tuple<Ts...>>::type;

Uso:

using ThirdType = NthTypeOf<2, Ts...>;


Podemos implementar una función simple para obtener el parámetro nth directamente sin ninguna llamada recursiva pero muchas operaciones de tipo puro en tiempo de compilación. Miremos el código de la llave en primer lugar:

template<class...Ts> struct GetImp { template<class T, class...Us> static decltype(auto) impl(Ts&&..., T&& obj, Us&&...) { return std::forward<T>(obj); } }; template<size_t n, class...Ts> decltype(auto) get(Ts&&...args) { static_assert(n<sizeof...(args), "index over range"); return Transform<GetImp, Before_s<n, Seq<Ts...>> > ::impl(std::forward<Ts>(args)...); }

¿Qué significa Transform?

Por ejemplo, si tenemos un tipo T que es std::tuple<int,double,float> , entonces Transform<GetImp,T> sería GetImp<int,double,float> . tenga en cuenta que defino otra estructura vacía "Seq" en lugar de std::tuple para hacer lo mismo con menos tiempo de compilación. (De hecho, ambos podrían compilarse muy rápidamente, pero supongo que una estructura vacía sería más efectiva) Entonces Before_s<n,Seq<Ts...>> generan una Seq<?> Y luego la transformamos en GetImp, para que podamos saber qué tipo de parámetros son [0] ~ [n-1], y luego soltarlos off para indexar el nth parámetro directamente. Por ejemplo, Before_s<3,Seq<T0,T1,T2,T3,T4...>> es Seq<T0,T1,T2> , Before_s<2,Seq<T0,T1,T2,T3,T4...>> es Seq<T0,T1> etc. Usamos Before_s para tratar con nuestro tipo de Seq para reducir el tiempo de compilación cuando usamos una meta función para implementar otra meta función para menos tiempo de compilación.

Implementación

#define OMIT_T(...) typename __VA_ARGS__::type template<class...Args> struct Seq { }; template< template<class...> class Dst > struct TransformImp{ template< template<class...>class Src, class...Args > static Dst<Args...> from(Src<Args...>&&); }; template< template<class...> class Dst, class T> using Transform = decltype(TransformImp<Dst>::from(std::declval<T>())); template<class T> using Seqfy = Transform<Seq, T>; template<class...>struct MergeImp; template<class...Ts, class...Others> struct MergeImp<Seq<Ts...>, Seq<Others...>> { using type = Seq<Ts..., Others...>; }; template<class first, class second> using Merge = OMIT_T(MergeImp<Seqfy<first>, Seqfy<second> >); template<class T, class U> using Merge_s = OMIT_T(MergeImp<T, U>); template<size_t, class...>struct BeforeImp; template<size_t n, class T, class...Ts> struct BeforeImp<n, Seq<T, Ts...>> { static_assert(n <= sizeof...(Ts)+1, "index over range"); using type = Merge_s<Seq<T>, OMIT_T(BeforeImp<n - 1, Seq<Ts...>>)>; }; template<class T, class...Ts> struct BeforeImp<1, Seq<T, Ts...>> { using type = Seq<T>; }; template<class T, class...Ts> struct BeforeImp<0, Seq<T, Ts...>> { using type = Seq<>; }; template<size_t n> struct BeforeImp<n, Seq<>> { using type = Seq<>; }; template<size_t n, class T> using Before = OMIT_T(BeforeImp<n, Seqfy<T>>); template<size_t n, class T> using Before_s = OMIT_T(BeforeImp<n, T>);

Editado: Implementación avanzada

No necesitamos utilizar Before_s para calcular n-1 tipos antes de nth type, en cambio, podemos ignorarlos:

struct EatParam{ constexpr EatParam(...)noexcept{} }; template<size_t n> struct GenSeqImp { using type = Merge_s<OMIT_T(GenSeqImp<n / 2>), OMIT_T(GenSeqImp<n - n / 2>)>; }; template<> struct GenSeqImp<0> { using type = Seq<>; }; template<> struct GenSeqImp<1> { using type = Seq<EatParam>; }; template<size_t n> using GenSeq = OMIT_T(GenSeqImp<n>); template<class...Ts> struct GetImp { template<class T> static constexpr decltype(auto) impl(Ts&&..., T&& obj, ...)noexcept { return std::forward<T>(obj); } }; template<size_t n, class...Ts> constexpr decltype(auto) get(Ts&&...args)noexcept { static_assert(n<sizeof...(args), "index over range."); //return Transform<GetImp, Before_s<n, Seq<Ts...>> > return Transform<GetImp, GenSeq<n>> ::impl(std::forward<Ts>(args)...); }

Además, there un artículo muy interesante sobre la implementación de obtener el enésimo tipo:

Gracias por su trabajo, no sabía que podríamos usar (...) para hacer el hack antes.


¿Acceder al elemento N-th?

Usando std::forward_as_tuple :

template <int I, class... Ts> decltype(auto) get(Ts&&... ts) { return std::get<I>(std::forward_as_tuple(ts...)); }

Ejemplo de uso:

template<class...Ts> void foo(Ts&&...ts){ auto& first = get<0>(ts...); auto second = get<1>(ts...); first = ''H''; second = ''E''; (std::cout << ... << ts); } foo(''h'',''e'',''l'',''l'',''o''); // prints "Hello"

Esta respuesta es para complementar la respuesta de Emile Cormier, que da solo el tipo n-ésimo.