Diberikan lambda, apakah mungkin untuk mengetahui tipe parameter dan tipe kembaliannya? Jika ya, bagaimana caranya?
Pada dasarnya, saya ingin lambda_traits
yang dapat digunakan dengan cara berikut:
auto lambda = [](int i) { return long(i*10); };
lambda_traits<decltype(lambda)>::param_type i; //i should be int
lambda_traits<decltype(lambda)>::return_type l; //l should be long
Motivasi di baliknya adalah saya ingin menggunakan lambda_traits
template fungsi yang menerima lambda sebagai argumen, dan saya perlu mengetahui jenis parameternya dan jenis kembalian di dalam fungsi:
template<typename TLambda>
void f(TLambda lambda)
{
typedef typename lambda_traits<TLambda>::param_type P;
typedef typename lambda_traits<TLambda>::return_type R;
std::function<R(P)> fun = lambda; //I want to do this!
//...
}
Untuk saat ini, kita dapat berasumsi bahwa lambda membutuhkan satu argumen.
Awalnya, saya mencoba bekerja dengan std::function
sebagai:
template<typename T>
A<T> f(std::function<bool(T)> fun)
{
return A<T>(fun);
}
f([](int){return true;}); //error
Namun hal itu jelas akan memberikan error. Jadi saya mengubahnya ke TLambda
versi template fungsi dan ingin membangun std::function
objek di dalam fungsi (seperti yang ditunjukkan di atas).
Jawaban:
Lucu, saya baru saja menulis
function_traits
implementasi berdasarkan Mengkhususkan template pada lambda di C ++ 0x yang dapat memberikan tipe parameter. Triknya, seperti yang dijelaskan dalam jawaban dalam pertanyaan itu, adalah dengan menggunakan lambda's .decltype
operator()
template <typename T> struct function_traits : public function_traits<decltype(&T::operator())> {}; // For generic types, directly use the result of the signature of its 'operator()' template <typename ClassType, typename ReturnType, typename... Args> struct function_traits<ReturnType(ClassType::*)(Args...) const> // we specialize for pointers to member function { enum { arity = sizeof...(Args) }; // arity is the number of arguments. typedef ReturnType result_type; template <size_t i> struct arg { typedef typename std::tuple_element<i, std::tuple<Args...>>::type type; // the i-th argument is equivalent to the i-th tuple element of a tuple // composed of those arguments. }; }; // test code below: int main() { auto lambda = [](int i) { return long(i*10); }; typedef function_traits<decltype(lambda)> traits; static_assert(std::is_same<long, traits::result_type>::value, "err"); static_assert(std::is_same<int, traits::arg<0>::type>::value, "err"); return 0; }
Perhatikan bahwa solusi ini tidak berfungsi untuk lambda umum seperti
[](auto x) {}
.sumber
tuple_element
, terima kasih.const
, untuk lambda yang dideklarasikanmutable
([]() mutable -> T { ... }
).operator()
tidak dengan implementasi ini.auto
bukan tipe, jadi itu tidak akan pernah menjadi jawaban untuktraits::template arg<0>::type
Meskipun saya tidak yakin ini benar-benar standar yang sesuai, ideone menyusun kode berikut:
template< class > struct mem_type; template< class C, class T > struct mem_type< T C::* > { typedef T type; }; template< class T > struct lambda_func_type { typedef typename mem_type< decltype( &T::operator() ) >::type type; }; int main() { auto l = [](int i) { return long(i); }; typedef lambda_func_type< decltype(l) >::type T; static_assert( std::is_same< T, long( int )const >::value, "" ); }
Namun, ini hanya menyediakan tipe fungsi, jadi tipe hasil dan parameter harus diekstraksi darinya. Jika Anda dapat menggunakan
boost::function_traits
,result_type
danarg1_type
akan memenuhi tujuannya. Karena ideone tampaknya tidak memberikan dorongan dalam mode C ++ 11, saya tidak dapat memposting kode sebenarnya, maaf.sumber
Metode spesialisasi yang ditunjukkan dalam jawaban @KennyTMs dapat diperluas untuk mencakup semua kasus, termasuk lambda variadic dan mutable:
template <typename T> struct closure_traits : closure_traits<decltype(&T::operator())> {}; #define REM_CTOR(...) __VA_ARGS__ #define SPEC(cv, var, is_var) \ template <typename C, typename R, typename... Args> \ struct closure_traits<R (C::*) (Args... REM_CTOR var) cv> \ { \ using arity = std::integral_constant<std::size_t, sizeof...(Args) >; \ using is_variadic = std::integral_constant<bool, is_var>; \ using is_const = std::is_const<int cv>; \ \ using result_type = R; \ \ template <std::size_t i> \ using arg = typename std::tuple_element<i, std::tuple<Args...>>::type; \ }; SPEC(const, (,...), 1) SPEC(const, (), 0) SPEC(, (,...), 1) SPEC(, (), 0)
Demo .
Perhatikan bahwa arity tidak disesuaikan untuk variadic
operator()
s. Sebaliknya orang juga bisa mempertimbangkanis_variadic
.sumber
Jawaban yang diberikan oleh @KennyTMs berfungsi dengan baik, namun jika lambda tidak memiliki parameter, menggunakan indeks arg <0> tidak dapat dikompilasi. Jika ada orang lain yang mengalami masalah ini, saya memiliki solusi sederhana (lebih sederhana daripada menggunakan solusi terkait SFINAE, yaitu).
Tambahkan saja void ke akhir tupel di arg struct setelah tipe argumen variadic. yaitu
template <size_t i> struct arg { typedef typename std::tuple_element<i, std::tuple<Args...,void>>::type type; };
karena arity tidak bergantung pada jumlah parameter template yang sebenarnya, yang sebenarnya tidak akan salah, dan jika 0 maka setidaknya arg <0> akan tetap ada dan Anda dapat melakukannya sesuka Anda. Jika Anda sudah berencana untuk tidak melebihi indeks
arg<arity-1>
maka itu tidak akan mengganggu implementasi Anda saat ini.sumber