function traits.
獲取函數或成員函數的返回類型,參數類型,參數長度,類類型。
函數參數列表推斷基於typelist:http://www.cnblogs.com/flytrace/p/3551414.html
先看一個普通函數非const的特化:
template<typename R, typename... Args> struct function_traits<R (Args...)> { typedef R return_type; typedef typelist<Args...> arglist; enum { arg_count = sizeof...(Args) }; template<unsigned int N> struct arg { typedef typename at<N, arglist>::type type; }; };
使用:
int testfunc1(char) { return 1; } int main() { bool b; b = std::is_same< typename function_traits<int(double)>::return_type, int>::value; std::cout << "is same: " << b << std::endl; b = std::is_same< typename function_traits<decltype(testfunc1)>::arg<0>::type, char>::value; std::cout << "is same: " << b << std::endl; }
對於各種參數類型的普通函數,都能正確推斷。但重載函數的情形需要我們考慮。如下我們增加testfunc1的重載版本:
bool testfunc1(double, char) { return false; }
此時decltype(testfunc1)是無法編譯通過的。這並不是我們的function_traits有問題。而是在沒信息的情況下,decltype是無法選擇testfunc1的重載版本的。除非我們在function_traits顯式特化。
函數指針的function_traits也會遇到重載問題,如下是針對函數指針的function_traits:
template<typename R, typename... Args> struct function_traits<R (*)(Args...)> { typedef R return_type; typedef typelist<Args...> arglist; enum { arg_count = sizeof...(Args) }; template<unsigned int N> struct arg { typedef typename at<N, arglist>::type type; }; };
decltye(&testfunc1)也是無法編譯通過的。很顯然,你自己作為編譯器作者的話,若是沒有額外的信息,讓你使用decltype去推斷一個可重載的函數類型,你怎么能夠知道用戶希望得到哪個類型?除了顯示特化以提供給編譯器信息外,對於函數指針,我們還可以提前轉換,顯式給以類型信息供編譯器推斷,如下:
int (*castfunc)(char) = &testfunc1;
b = std::is_same< typename function_traits<decltype(castfunc)>::arg<0>::type, char>::value; std::cout << "is same: " << b << std::endl;
castfunc1在定義時得到了testfunc1正確的重載類型,因此decltype在推斷castfunc時就有了信息來選擇正確的類型。
這並不是一個程序技術問題,更算是一個邏輯問題,就好像面對有多個定義的單詞,沒有上下文你是無法知道它要代表什么意思的。
這種顯示轉換並不會帶給我們太多困擾。因為使用function_traits的場景,基本上是一種延遲推斷手段。比如得到消息后,使用泛型手法分發消息處理。而消息處理函數我們在注冊的時候肯定是知道函數類型的,在注冊時我們就已經可以顯示轉換這個函數指針而不會遇到重載問題了。直接使用decltype(testfunc1)好像在我們測試function_traits時才會遇到,嗯,另一個人也遇到了,不然我不會試驗。。。
然而確實存在一個可能,使我們可以傳入testfunc1,而不用給予完整類型信息,雖然不適用於function_traits的情況。如下:
template<typename ...Args> struct OverloadResolved { template<typename R> static auto static_doit( R (*f) (Args...), Args ... args ) -> R { return f(args...);} }; template<typename ...Args> auto deduce(Args...) -> OverloadResolved<Args...> { return OverloadResolved<Args...>(); } template<typename T> struct dummy : public T { }; #define doit(f, ...) ( dummy<decltype(deduce( __VA_ARGS__ ))> :: static_doit(f, __VA_ARGS__) )
使用:
char aa = 'a'; double ff = 0.1; std::cout << doit(testfunc1, aa) << " " << doit(testfunc1, ff, aa) << std::endl;
可以看到,雖然testfunc1有2個重載版本,但仍能正確的執行testfunc1(aa)和testfunc1(ff, aa).
當然因為此處給出了參數信息。這是一個運行時方案,而function_traits要求我們在編譯期就推斷。
以下添加類成員函數的function_traits:
template <typename R, typename T, typename... Args> struct function_traits<R (T::*)(Args...)> { typedef T class_type; typedef R return_type; typedef typelist<Args...> arglist; enum { arg_count = sizeof...(Args) }; template<unsigned int N> struct arg { typedef typename at<N, arglist>::type type; }; };
還需要添加const,volatile修飾符的。以下是更完整的版本:

template<typename T> struct function_traits; template<typename R, typename... Args> struct function_traits<R (Args...)> { typedef R return_type; typedef typelist<Args...> arglist; enum { arg_count = sizeof...(Args) }; template<unsigned int N> struct arg { typedef typename at<N, arglist>::type type; }; }; template<typename R, typename... Args> struct function_traits<R (Args...) const> { typedef R return_type; typedef typelist<Args...> arglist; enum { arg_count = sizeof...(Args) }; template<unsigned int N> struct arg { typedef typename at<N, arglist>::type type; }; }; template<typename R, typename... Args> struct function_traits<R (Args...) volatile> { typedef R return_type; typedef typelist<Args...> arglist; enum { arg_count = sizeof...(Args) }; template<unsigned int N> struct arg { typedef typename at<N, arglist>::type type; }; }; template<typename R, typename... Args> struct function_traits<R (Args...) const volatile> { typedef R return_type; typedef typelist<Args...> arglist; enum { arg_count = sizeof...(Args) }; template<unsigned int N> struct arg { typedef typename at<N, arglist>::type type; }; }; template<typename R, typename... Args> struct function_traits<R (*)(Args...)> { typedef R return_type; typedef typelist<Args...> arglist; enum { arg_count = sizeof...(Args) }; template<unsigned int N> struct arg { typedef typename at<N, arglist>::type type; }; }; template <typename R, typename T, typename... Args> struct function_traits<R (T::*)(Args...)> { typedef T class_type; typedef R return_type; typedef typelist<Args...> arglist; enum { arg_count = sizeof...(Args) }; template<unsigned int N> struct arg { typedef typename at<N, arglist>::type type; }; }; template <typename R, typename T, typename... Args> struct function_traits<R (T::*)(Args...) const> { typedef T class_type; typedef R return_type; typedef typelist<Args...> arglist; enum { arg_count = sizeof...(Args) }; template<unsigned int N> struct arg { typedef typename at<N, arglist>::type type; }; }; template <typename R, typename T, typename... Args> struct function_traits<R (T::*)(Args...) volatile> { typedef T class_type; typedef R return_type; typedef typelist<Args...> arglist; enum { arg_count = sizeof...(Args) }; template<unsigned int N> struct arg { typedef typename at<N, arglist>::type type; }; }; template <typename R, typename T, typename... Args> struct function_traits<R (T::*)(Args...) const volatile> { typedef T class_type; typedef R return_type; typedef typelist<Args...> arglist; enum { arg_count = sizeof...(Args) }; template<unsigned int N> struct arg { typedef typename at<N, arglist>::type type; }; };