Это не скомпилируется function_type_deducer([](){}).describe_me();
Это сработало бы, если бы function_type_deducer
не был шаблоном. :) Не захватывающие лямбды (пустые []
) неявно преобразуются в указатели функций. К сожалению, неявные преобразования не принимаются во внимание при выводе аргументов некоторого шаблона. См. этот вопрос для получения дополнительной информации (обратите внимание, что мой ответ не совсем правильный, как указывают комментарии).
Он не замечает, что между x и y есть небольшая разница, поскольку y принимает строку &, где x принимает строку.
Это не проблема с функцией, это проблема с typeid
, как показывает этот простой тестовый код:
template<class T>
void x(void(T)){
T v;
(void)v;
}
void f1(int){}
void f2(int&){}
int main(){
x(f1);
x(f2);
}
Живой пример на Ideone . Выход:
ошибка: 'v' объявлена как ссылка, но не инициализирована
Простым исправлением может быть использование диспетчеризации тегов:
#include <type_traits> // is_reference
#include <iostream>
#include <typeinfo>
template<class T>
void print_name(std::true_type){
std::cout << "reference to " << typeid(T).name();
}
template<class T>
void print_name(std::false_type){
std::cout << typeid(T).name();
}
template<class T>
void print_name(){
print_name(typename std::is_reference<T>::type());
}
И позвоните print_name<NextArg>()
вместо typeid(NextArg).name()
.
Я заново изобрел колесо?
Да, вроде как и нет, вы этого не сделали. Boost.Function предоставляет typedefs для всех аргументов (стиль argN_type
), а также статическую константу arity
для их числа. Тем не менее, вы не можете легко получить доступ к этим typedefs в общем. Вам понадобится окольный способ, чтобы случайно не получить доступ к несуществующим. Идея tuple
работает лучше всего, однако она может быть написана лучше. Вот модифицированная версия того, что я однажды написал:
#include <tuple>
#include <type_traits>
#include <iostream>
#include <typeinfo>
namespace detail{
template<class T>
std::ostream& print_name(std::ostream& os);
template<class T>
std::ostream& print_pointer(std::ostream& os, std::true_type){
typedef typename std::remove_pointer<T>:: type np_type;
os << "pointer to ";
return print_name<np_type>(os);
}
template<class T>
std::ostream& print_pointer(std::ostream& os, std::false_type){
return os << typeid(T).name();
}
template<class T>
std::ostream& print_name(std::ostream& os, std::true_type){
return os << "reference to " << typeid(T).name();
}
template<class T>
std::ostream& print_name(std::ostream& os, std::false_type){
return print_pointer<T>(os, typename std::is_pointer<T>::type());
}
template<class T>
std::ostream& print_name(std::ostream& os){
return print_name<T>(os, typename std::is_reference<T>::type());
}
// to workaround partial function specialization
template<unsigned> struct int2type{};
template<class Tuple, unsigned I>
std::ostream& print_types(std::ostream& os, int2type<I>){
typedef typename std::tuple_element<I,Tuple>::type type;
print_types<Tuple>(os, int2type<I-1>()); // left-folding
os << ", ";
return print_name<type>(os);
}
template<class Tuple>
std::ostream& print_types(std::ostream& os, int2type<0>){
typedef typename std::tuple_element<0,Tuple>::type type;
return print_name<type>(os);
}
} // detail::
template<class R, class... Args>
struct function_info{
typedef R result_type;
typedef std::tuple<Args...> argument_tuple;
static unsigned const arity = sizeof...(Args);
void describe_me(std::ostream& os = std::cout) const{
using namespace detail;
os << "I return '"; print_name<result_type>(os);
os << "' and I take '" << arity << "' arguments. They are: \n\t'";
print_types<argument_tuple>(os, int2type<arity-1>()) << "'\n";
}
};
Живой пример на Ideone . Выход:
main: I return 'i' and I take '2' arguments. They are:
'i, pointer to pointer to c'
x: I return 'Ss' and I take '3' arguments. They are:
'i, Ss, c'
y: I return 'Ss' and I take '3' arguments. They are:
'i, reference to Ss, c'