问题
My goal is to write a simple generic function for registering converters for arbitrary C++ types. For simplicity I'll just print C++ type names. I'd like to be able to call my generic print_type_name
function for any types, including multiple types at once (variadic):
template <typename T>
void print_type_name(void)
{
std::cout << typeid(T).name() << std::endl;
}
This works fine for things like this:
print_type_name<int>();
print_type_name<std::string>();
print_type_name<std::vector<std::complex<float> > >();
However, I need to be able to call this function for each type in a variadic template, e.g. (when expanded):
print_type_name<int, std::string, std::vector<std::complex<float> > >();
Here's what I've come up with, but it's rather clunky:
template <typename ...TS>
void noop(TS... ts) { }
template <typename T>
int real_print_type_name(void) {
std::cout << typeid(T).name() << std::endl;
return 0;
}
template <typename ...TS>
void print_type_name(void) {
noop(real_print_type_name<TS>()...);
}
Which allows for the following:
template <typename ...TS>
void other_function(void) {
print_type_name<TS...>();
}
Notice the useless noop
function and the int
return type of
real_print_type_name
, both of which I had to add in order to expand
the parameter pack. Is there a cleaner way of doing this?
回答1:
template <typename ...TS>
void print_type_name() {
using expander = int[];
(void) expander{ 0, (std::cout << typeid(TS).name() << '\n', 0)... };
}
Or, C++17-style:
template <typename ...TS>
void print_type_name(void) {
(std::cout << ... << (typeid(TS).name() + "\n"s));
}
Demo.
回答2:
Here is a helper function. It uses random dark magic, but its name is pretty clear:
void do_in_order() {}
template<class...Fs>
void do_in_order( Fs&&...fs ) {
using discard=int[];
(void)discard{0, (void(
std::forward<Fs>(fs)()
),0)... };
}
or in C++17:
template<class...Fs>
void do_in_order( Fs&&...fs ) {
(void(std::forward<Fs>(fs)())...);
}
(much nicer).
which hides any uglyness. It takes a set of void()
callables and calls
them left to right -- it does the tasks in order, like it says on the tin.
Then print_type_names
becomes:
template<class...Ts>
void print_type_names() {
do_in_order( print_type_name<Ts>... );
}
or
template<class...Ts>
void print_type_names() {
do_in_order( [&]{
std::cout << typeid(Ts).name() << std::endl;
}... );
}
if you don't want to use the single print_type_name
function and want to inline it.
Note that some non-conforming compilers complain about having an entire lambda be expanded in a ...
.
live example
回答3:
I think you can do this:
void print_type_name()
{
std::cout<<"\n";
}
template <typename T>
void print_type_name(const T& t)
{
std::cout<<t<<" : of type "<<typeid(t).name()<<"\n";
}
template <typename T1, typename... Ts>
void print_type_name(const T1& t1, const Ts&... ts)
{
// Head
std::cout<<t1<<" : of type "<<typeid(t1).name()<<", ";
// Tail
print_type_name(ts...);
}
Whether it's clearer or not I don't know.
来源:https://stackoverflow.com/questions/28547456/call-void-function-for-each-template-type-in-a-variadic-templated-function