问题
Lets say I have a function that takes just a type template parameter, I cannot change it's definition/implementation.
template < typename T >
void do_it();
Now I have a typelist defined a usual way, can't change it either:
template< typename ...Ts >
struct typelist;
I want to implement a function that takes a typelist, and runs do_it() on every type:
template< typename List >
void do_them();
The only solution I found up 'till now is:
template< typename T >
void do_them_impl()
{
do_it<T>();
}
template< typename T, typename Ts...>
void do_them_impl()
{
do_it<T>();
do_them_impl<Ts...>();
}
template< template < typename...> class List, typename ...Ts >
void do_them_extract( List<Ts...>&& )
{
do_them_impl< Ts >();
}
template< typename List >
void do_them()
{
do_them_impl( List{} );
}
But that requires 4(!) functions for each case I want to create a single do_them
function. I will require quite a few of those, and I don't want to write a quad of functions for each. Am I missing something?
C++14 welcome, C++17 solutions too, but marked as such.
回答1:
In C++14 you can use some awful tricks to introduce a valid pack expansion context:
template< template < typename...> class List, typename ...Ts >
void do_them_impl( List<Ts...>&& )
{
(void)std::initializer_list<int> {
(do_it<Ts>(), 0)...
};
}
template< typename List >
void do_them()
{
do_them_impl( List{} );
}
This allows you to avoid recursive template instantiation, which is generally more expensive.
Live Demo
In C++17 you can use fold expressions:
template< template < typename...> class List, typename ...Ts >
void do_them_impl( List<Ts...>&& )
{
(do_it<Ts>(), ...);
}
template< typename List >
void do_them()
{
do_them_impl( List{} );
}
Live Demo
回答2:
Here is a solution that takes advantage of C++14's generic lambdas:
template <typename T>
struct type_ { using type = T; };
template <typename Type>
using type = typename Type::type;
template <typename List>
struct map_;
template <template <typename...> typename Container, typename... Ts>
struct map_<Container<Ts...>>
{
template <typename Fun>
void operator()(Fun&& fun)
{
(void)((int[sizeof...(Ts)]){(fun(type_<Ts>{}), 0)...});
}
};
template <typename List>
auto map = map_<List>{};
Then for each function
#include <iostream>
#include <cxxabi.h>
#include <typeinfo>
template <typename T>
const char * type_name()
{
return abi::__cxa_demangle(typeid(T).name(), nullptr, nullptr, nullptr);
}
template <typename T>
void do_it()
{
std::cout << type_name<T>() << std::endl;
}
you can write:
template <typename List>
void do_them()
{
map<List>([](auto v){ do_it<type<decltype(v)>>(); });
}
template <typename... Ts>
struct typelist {};
int main()
{
do_them<typelist<int, char, bool>>();
return 0;
}
Compiling with -O3 gives the same assembly as if we simply called do_it<int>
, do_it<char>
, do_it<bool>
in succession.
来源:https://stackoverflow.com/questions/42002126/unpacking-a-typelist