How to detect the first and the last argument in the variadic templates?
For the 1st argument it is easy (just compare sizeof...(T)
with 0), but is ther
Here's another set of code with a convenience function return_type
that you could use to access any type at a specific index in a varadic template list ... you could then adapt the call to return_type
so that you get the first and the last arguments (i.e., the first argument will be at 0, and the last argument will be at sizeof...(TypeList)
):
template<typename T>
struct type_id_struct
{
typedef T type;
T object_instance;
};
template<int N, typename... TypeList>
struct reduce {};
template<int N, typename T1, typename... TypeList>
struct reduce<N, T1, TypeList...>
{
typedef typename reduce<N - 1, TypeList... >::type type;
};
template<typename T1, typename... TypeList>
struct reduce<0, T1, TypeList...>
{
typedef T1 type;
};
//convenience function
template<int N, typename... TypeList>
type_id_struct<typename reduce<N, TypeList...>::type> return_type()
{
return type_id_struct<typename reduce<N, TypeList...>::type>();
}
Here's an example of using the convenience function return_type
in actual code to determine the Nth template argument in a variadic template:
int main()
{
auto type_returned = return_type<2, int, double, char>();
std::cout << typeid(type_returned.object_instance).name() << std::endl;
return 0;
}
In this case, since the int
template argument to return_type
is 2
, you'll get the char
type as the output. Any number over 2
will cause an overflow that will create a compile rather than runtime error. As noted, you could adapt it so that it's wrapped inside a function in a structure that will allow you to access the types in the variadic template for that specific structure instance using the sizeof...(TypeList) - 1
applied to an enum. For instance:
template<typename... TypeList>
struct an_object
{
enum { first = 0, last = (sizeof...(TypeList) - 1) };
template<int N>
auto wrapper() -> decltype(return_type<N, TypeList...>())
{
return return_type<N, TypeList...>();
}
};
//...more code
int main()
{
an_object<int, double, char> a;
auto r_type1 = a.wrapper<an_object<int, double, char>::first>();
std::cout << typeid(r_type1.object_instance).name() << std::endl;
auto r_type2 = a.wrapper<an_object<int, double, char>::last>();
std::cout << typeid(r_type2.object_instance).name() << std::endl;
return 0;
}
I'm not positive if this is what you want. But here are two utilities named first
and last
that take variadic templates and typedef the first and last type respectively:
#include <iostream>
#include <typeinfo>
template <class T1, class ...T>
struct first
{
typedef T1 type;
};
template <class T1, class ...T>
struct last
{
typedef typename last<T...>::type type;
};
template <class T1>
struct last<T1>
{
typedef T1 type;
};
template <class ...T>
struct A
{
typedef typename first<T...>::type first;
typedef typename last<T...>::type last;
};
struct B1 {};
struct B2 {};
struct B3 {};
int main()
{
typedef A<B1, B2, B3> T;
std::cout << typeid(T::first).name() << '\n';
std::cout << typeid(T::last).name() << '\n';
}