Pertimbangkan kasus fungsi templated dengan argumen template variadic:
template<typename Tret, typename... T> Tret func(const T&... t);
Sekarang, saya memiliki tupel t
nilai. Bagaimana cara memanggil func()
menggunakan nilai tupel sebagai argumen? Saya telah membaca tentang bind()
objek fungsi, dengan call()
fungsi, dan juga apply()
fungsi di beberapa dokumen yang sekarang sudah usang. Implementasi GNU GCC 4.4 tampaknya memiliki call()
fungsi di bind()
kelas, tetapi hanya ada sedikit dokumentasi tentang subjek tersebut.
Beberapa orang menyarankan peretasan rekursif yang ditulis tangan, tetapi nilai sebenarnya dari argumen templat variadic adalah dapat menggunakannya dalam kasus seperti di atas.
Adakah yang punya solusi untuk itu, atau petunjuk di mana membacanya?
integer_sequence
, lihat en.cppreference.com/w/cpp/utility/integer_sequenceinteger_sequence S
, Anda cukup memanggil fungsi Anda sebagaifunc(std::get<S>(tuple)...)
, dan biarkan kompilator menangani sisanya.Jawaban:
Ini kode saya jika ada yang tertarik
Pada dasarnya pada waktu kompilasi kompilator akan secara rekursif membuka gulungan semua argumen dalam berbagai panggilan fungsi inklusif <N> -> panggilan <N-1> -> panggilan ... -> panggilan <0> yang merupakan yang terakhir dan kompilator akan mengoptimalkannya. berbagai pemanggilan fungsi menengah untuk hanya menyimpan yang terakhir yang setara dengan func (arg1, arg2, arg3, ...)
Tersedia 2 versi, satu untuk fungsi yang dipanggil pada objek dan yang lainnya untuk fungsi statis.
#include <tr1/tuple> /** * Object Function Tuple Argument Unpacking * * This recursive template unpacks the tuple parameters into * variadic template arguments until we reach the count of 0 where the function * is called with the correct parameters * * @tparam N Number of tuple arguments to unroll * * @ingroup g_util_tuple */ template < uint N > struct apply_obj_func { template < typename T, typename... ArgsF, typename... ArgsT, typename... Args > static void applyTuple( T* pObj, void (T::*f)( ArgsF... ), const std::tr1::tuple<ArgsT...>& t, Args... args ) { apply_obj_func<N-1>::applyTuple( pObj, f, t, std::tr1::get<N-1>( t ), args... ); } }; //----------------------------------------------------------------------------- /** * Object Function Tuple Argument Unpacking End Point * * This recursive template unpacks the tuple parameters into * variadic template arguments until we reach the count of 0 where the function * is called with the correct parameters * * @ingroup g_util_tuple */ template <> struct apply_obj_func<0> { template < typename T, typename... ArgsF, typename... ArgsT, typename... Args > static void applyTuple( T* pObj, void (T::*f)( ArgsF... ), const std::tr1::tuple<ArgsT...>& /* t */, Args... args ) { (pObj->*f)( args... ); } }; //----------------------------------------------------------------------------- /** * Object Function Call Forwarding Using Tuple Pack Parameters */ // Actual apply function template < typename T, typename... ArgsF, typename... ArgsT > void applyTuple( T* pObj, void (T::*f)( ArgsF... ), std::tr1::tuple<ArgsT...> const& t ) { apply_obj_func<sizeof...(ArgsT)>::applyTuple( pObj, f, t ); } //----------------------------------------------------------------------------- /** * Static Function Tuple Argument Unpacking * * This recursive template unpacks the tuple parameters into * variadic template arguments until we reach the count of 0 where the function * is called with the correct parameters * * @tparam N Number of tuple arguments to unroll * * @ingroup g_util_tuple */ template < uint N > struct apply_func { template < typename... ArgsF, typename... ArgsT, typename... Args > static void applyTuple( void (*f)( ArgsF... ), const std::tr1::tuple<ArgsT...>& t, Args... args ) { apply_func<N-1>::applyTuple( f, t, std::tr1::get<N-1>( t ), args... ); } }; //----------------------------------------------------------------------------- /** * Static Function Tuple Argument Unpacking End Point * * This recursive template unpacks the tuple parameters into * variadic template arguments until we reach the count of 0 where the function * is called with the correct parameters * * @ingroup g_util_tuple */ template <> struct apply_func<0> { template < typename... ArgsF, typename... ArgsT, typename... Args > static void applyTuple( void (*f)( ArgsF... ), const std::tr1::tuple<ArgsT...>& /* t */, Args... args ) { f( args... ); } }; //----------------------------------------------------------------------------- /** * Static Function Call Forwarding Using Tuple Pack Parameters */ // Actual apply function template < typename... ArgsF, typename... ArgsT > void applyTuple( void (*f)(ArgsF...), std::tr1::tuple<ArgsT...> const& t ) { apply_func<sizeof...(ArgsT)>::applyTuple( f, t ); } // *************************************** // Usage // *************************************** template < typename T, typename... Args > class Message : public IMessage { typedef void (T::*F)( Args... args ); public: Message( const std::string& name, T& obj, F pFunc, Args... args ); private: virtual void doDispatch( ); T* pObj_; F pFunc_; std::tr1::tuple<Args...> args_; }; //----------------------------------------------------------------------------- template < typename T, typename... Args > Message<T, Args...>::Message( const std::string& name, T& obj, F pFunc, Args... args ) : IMessage( name ), pObj_( &obj ), pFunc_( pFunc ), args_( std::forward<Args>(args)... ) { } //----------------------------------------------------------------------------- template < typename T, typename... Args > void Message<T, Args...>::doDispatch( ) { try { applyTuple( pObj_, pFunc_, args_ ); } catch ( std::exception& e ) { } }
sumber
tr1
hal dapat diambil sekarang dengan c ++ 11Di C ++ 17 Anda dapat melakukan ini:
std::apply(the_function, the_tuple);
Ini sudah berfungsi di Clang ++ 3.9, menggunakan std :: eksperimental :: apply.
Menanggapi komentar yang mengatakan bahwa ini tidak akan berfungsi jika
the_function
diberi template, berikut ini adalah solusinya:#include <tuple> template <typename T, typename U> void my_func(T &&t, U &&u) {} int main(int argc, char *argv[argc]) { std::tuple<int, float> my_tuple; std::apply([](auto &&... args) { my_func(args...); }, my_tuple); return 0; }
Solusi ini adalah solusi yang disederhanakan untuk masalah umum dalam melewatkan set kelebihan beban dan templat fungsi di mana fungsi diharapkan. Solusi umum (yang menangani penerusan sempurna, ketegasan, dan noexcept) disajikan di sini: https://blog.tartanllama.xyz/passing-overload-sets/ .
sumber
the_function
diberi template.std::apply(add_generic<float>, std::make_pair(2.0f, 3.0f));
Di C ++ ada banyak cara untuk memperluas / membongkar tupel dan menerapkan elemen tupel tersebut ke fungsi template variadic. Berikut adalah kelas pembantu kecil yang membuat array indeks. Ini banyak digunakan dalam metaprogramming template:
// ------------- UTILITY--------------- template<int...> struct index_tuple{}; template<int I, typename IndexTuple, typename... Types> struct make_indexes_impl; template<int I, int... Indexes, typename T, typename ... Types> struct make_indexes_impl<I, index_tuple<Indexes...>, T, Types...> { typedef typename make_indexes_impl<I + 1, index_tuple<Indexes..., I>, Types...>::type type; }; template<int I, int... Indexes> struct make_indexes_impl<I, index_tuple<Indexes...> > { typedef index_tuple<Indexes...> type; }; template<typename ... Types> struct make_indexes : make_indexes_impl<0, index_tuple<>, Types...> {};
Sekarang kode yang melakukan pekerjaan itu tidak terlalu besar:
// ----------UNPACK TUPLE AND APPLY TO FUNCTION --------- #include <tuple> #include <iostream> using namespace std; template<class Ret, class... Args, int... Indexes > Ret apply_helper( Ret (*pf)(Args...), index_tuple< Indexes... >, tuple<Args...>&& tup) { return pf( forward<Args>( get<Indexes>(tup))... ); } template<class Ret, class ... Args> Ret apply(Ret (*pf)(Args...), const tuple<Args...>& tup) { return apply_helper(pf, typename make_indexes<Args...>::type(), tuple<Args...>(tup)); } template<class Ret, class ... Args> Ret apply(Ret (*pf)(Args...), tuple<Args...>&& tup) { return apply_helper(pf, typename make_indexes<Args...>::type(), forward<tuple<Args...>>(tup)); }
Tes ditunjukkan di bawah ini:
// --------------------- TEST ------------------ void one(int i, double d) { std::cout << "function one(" << i << ", " << d << ");\n"; } int two(int i) { std::cout << "function two(" << i << ");\n"; return i; } int main() { std::tuple<int, double> tup(23, 4.5); apply(one, tup); int d = apply(two, std::make_tuple(2)); return 0; }
Saya bukan ahli besar dalam bahasa lain, tetapi saya rasa jika bahasa-bahasa ini tidak memiliki fungsi seperti itu di menu mereka, tidak ada cara untuk melakukan itu. Setidaknya dengan C ++ Anda bisa, dan menurut saya tidak terlalu rumit ...
sumber
template<class ... T> void three(T...) {}
dan mencoba menggunakan apply yang tidak dapat dikompilasi.Menurut saya ini adalah solusi paling elegan (dan diteruskan secara optimal):
#include <cstddef> #include <tuple> #include <type_traits> #include <utility> template<size_t N> struct Apply { template<typename F, typename T, typename... A> static inline auto apply(F && f, T && t, A &&... a) -> decltype(Apply<N-1>::apply( ::std::forward<F>(f), ::std::forward<T>(t), ::std::get<N-1>(::std::forward<T>(t)), ::std::forward<A>(a)... )) { return Apply<N-1>::apply(::std::forward<F>(f), ::std::forward<T>(t), ::std::get<N-1>(::std::forward<T>(t)), ::std::forward<A>(a)... ); } }; template<> struct Apply<0> { template<typename F, typename T, typename... A> static inline auto apply(F && f, T &&, A &&... a) -> decltype(::std::forward<F>(f)(::std::forward<A>(a)...)) { return ::std::forward<F>(f)(::std::forward<A>(a)...); } }; template<typename F, typename T> inline auto apply(F && f, T && t) -> decltype(Apply< ::std::tuple_size< typename ::std::decay<T>::type >::value>::apply(::std::forward<F>(f), ::std::forward<T>(t))) { return Apply< ::std::tuple_size< typename ::std::decay<T>::type >::value>::apply(::std::forward<F>(f), ::std::forward<T>(t)); }
Contoh penggunaan:
void foo(int i, bool b); std::tuple<int, bool> t = make_tuple(20, false); void m() { apply(&foo, t); }
Sayangnya GCC (setidaknya 4.6) gagal untuk mengkompilasi ini dengan "sorry, unimplemented: mangling overload" (yang berarti kompilator belum sepenuhnya mengimplementasikan spesifikasi C ++ 11), dan karena ia menggunakan template variadic, ia tidak akan bekerja di MSVC, jadi kurang lebih tidak berguna. Namun, begitu ada kompiler yang mendukung spesifikasi tersebut, itu akan menjadi IMHO pendekatan terbaik. (Catatan: tidak sulit untuk memodifikasi ini sehingga Anda dapat mengatasi kekurangan di GCC, atau menerapkannya dengan Boost Preprocessor, tetapi merusak keanggunan, jadi ini adalah versi yang saya posting.)GCC 4.7 sekarang mendukung kode ini dengan baik.
Edit: Ditambahkan maju sekitar panggilan fungsi aktual untuk mendukung formulir referensi rvalue * ini jika Anda menggunakan dentang (atau jika ada orang lain yang benar-benar berkeliling untuk menambahkannya).
Edit: Menambahkan maju yang hilang di sekitar objek fungsi di badan fungsi penerapan non-anggota. Terima kasih kepada pheedbaq karena telah menunjukkan bahwa itu hilang.
Sunting: Dan inilah versi C ++ 14 karena jauh lebih baik (belum benar-benar dikompilasi):
#include <cstddef> #include <tuple> #include <type_traits> #include <utility> template<size_t N> struct Apply { template<typename F, typename T, typename... A> static inline auto apply(F && f, T && t, A &&... a) { return Apply<N-1>::apply(::std::forward<F>(f), ::std::forward<T>(t), ::std::get<N-1>(::std::forward<T>(t)), ::std::forward<A>(a)... ); } }; template<> struct Apply<0> { template<typename F, typename T, typename... A> static inline auto apply(F && f, T &&, A &&... a) { return ::std::forward<F>(f)(::std::forward<A>(a)...); } }; template<typename F, typename T> inline auto apply(F && f, T && t) { return Apply< ::std::tuple_size< ::std::decay_t<T> >::value>::apply(::std::forward<F>(f), ::std::forward<T>(t)); }
Ini adalah versi untuk fungsi anggota (tidak banyak diuji!):
using std::forward; // You can change this if you like unreadable code or care hugely about namespace pollution. template<size_t N> struct ApplyMember { template<typename C, typename F, typename T, typename... A> static inline auto apply(C&& c, F&& f, T&& t, A&&... a) -> decltype(ApplyMember<N-1>::apply(forward<C>(c), forward<F>(f), forward<T>(t), std::get<N-1>(forward<T>(t)), forward<A>(a)...)) { return ApplyMember<N-1>::apply(forward<C>(c), forward<F>(f), forward<T>(t), std::get<N-1>(forward<T>(t)), forward<A>(a)...); } }; template<> struct ApplyMember<0> { template<typename C, typename F, typename T, typename... A> static inline auto apply(C&& c, F&& f, T&&, A&&... a) -> decltype((forward<C>(c)->*forward<F>(f))(forward<A>(a)...)) { return (forward<C>(c)->*forward<F>(f))(forward<A>(a)...); } }; // C is the class, F is the member function, T is the tuple. template<typename C, typename F, typename T> inline auto apply(C&& c, F&& f, T&& t) -> decltype(ApplyMember<std::tuple_size<typename std::decay<T>::type>::value>::apply(forward<C>(c), forward<F>(f), forward<T>(t))) { return ApplyMember<std::tuple_size<typename std::decay<T>::type>::value>::apply(forward<C>(c), forward<F>(f), forward<T>(t)); }
// Example: class MyClass { public: void foo(int i, bool b); }; MyClass mc; std::tuple<int, bool> t = make_tuple(20, false); void m() { apply(&mc, &MyClass::foo, t); }
sumber
apply
, mengapaf
tidak dibungkus denganstd::forward
panggilan, seperti pada tipe kembalian? Apakah tidak dibutuhkan?foo('x', true)
mengkompilasi ke kode assembly yang sama persisapply(foo, ::std::make_tuple('x', true))
dengan tingkat pengoptimalan apa pun selain -O0.integer_sequence
Anda bahkan mendapatkan implementasi yang hampir benarapply()
dalam contohnya. lihat jawaban saya di bawah ini.template<typename F, typename Tuple, std::size_t ... I> auto apply_impl(F&& f, Tuple&& t, std::index_sequence<I...>) { return std::forward<F>(f)(std::get<I>(std::forward<Tuple>(t))...); } template<typename F, typename Tuple> auto apply(F&& f, Tuple&& t) { using Indices = std::make_index_sequence<std::tuple_size<std::decay_t<Tuple>>::value>; return apply_impl(std::forward<F>(f), std::forward<Tuple>(t), Indices()); }
Ini diadaptasi dari draf C ++ 14 menggunakan index_sequence. Saya mungkin mengusulkan untuk menerapkan standar masa depan (TS).
sumber
Beritanya tidak terlihat bagus.
Setelah membaca draf standar yang baru saja dirilis , saya tidak melihat solusi bawaan untuk ini, yang memang tampak aneh.
Tempat terbaik untuk bertanya tentang hal-hal seperti itu (jika Anda belum melakukannya) adalah comp.lang.c ++. Dimoderatori, karena beberapa orang terlibat dalam penyusunan posting standar di sana secara teratur.
Jika Anda memeriksa utas ini , seseorang memiliki pertanyaan yang sama (mungkin itu Anda, dalam hal ini Anda akan menemukan jawaban keseluruhan ini sedikit membuat frustrasi!), Dan beberapa penerapan yang sangat buruk disarankan.
Saya hanya bertanya-tanya apakah akan lebih sederhana untuk membuat fungsi menerima a
tuple
, karena konversi dengan cara itu lebih mudah. Tapi ini menyiratkan bahwa semua fungsi harus menerima tupel sebagai argumen, untuk fleksibilitas maksimum, dan sehingga hanya menunjukkan keanehan karena tidak menyediakan perluasan paket argumen tupel ke fungsi bawaan.Perbarui: tautan di atas tidak berfungsi - coba tempel ini:
http://groups.google.com/group/comp.lang.c++.moderated/browse_thread/thread/750fa3815cdaac45/d8dc09e34bbb9661?lnk=gst&q=tuple+variadic#d8dc09e34bbb9661
sumber
Semua implementasi ini bagus. Tetapi karena penggunaan pointer ke compiler fungsi anggota sering tidak dapat menyebariskan panggilan fungsi target (setidaknya gcc 4.8 tidak bisa, apa pun yang terjadi, Mengapa gcc tidak bisa mengarahkan pointer fungsi yang dapat ditentukan? )
Tetapi hal-hal berubah jika mengirim penunjuk ke fungsi anggota sebagai argumen templat, bukan sebagai parameter fungsi:
/// from https://stackoverflow.com/a/9288547/1559666 template<int ...> struct seq {}; template<int N, int ...S> struct gens : gens<N-1, N-1, S...> {}; template<int ...S> struct gens<0, S...>{ typedef seq<S...> type; }; template<typename TT> using makeSeq = typename gens< std::tuple_size< typename std::decay<TT>::type >::value >::type; // deduce function return type template<class ...Args> struct fn_type; template<class ...Args> struct fn_type< std::tuple<Args...> >{ // will not be called template<class Self, class Fn> static auto type_helper(Self &self, Fn f) -> decltype((self.*f)(declval<Args>()...)){ //return (self.*f)(Args()...); return NULL; } }; template<class Self, class ...Args> struct APPLY_TUPLE{}; template<class Self, class ...Args> struct APPLY_TUPLE<Self, std::tuple<Args...>>{ Self &self; APPLY_TUPLE(Self &self): self(self){} template<class T, T (Self::* f)(Args...), class Tuple> void delayed_call(Tuple &&list){ caller<T, f, Tuple >(forward<Tuple>(list), makeSeq<Tuple>() ); } template<class T, T (Self::* f)(Args...), class Tuple, int ...S> void caller(Tuple &&list, const seq<S...>){ (self.*f)( std::get<S>(forward<Tuple>(list))... ); } }; #define type_of(val) typename decay<decltype(val)>::type #define apply_tuple(obj, fname, tuple) \ APPLY_TUPLE<typename decay<decltype(obj)>::type, typename decay<decltype(tuple)>::type >(obj).delayed_call< \ decltype( fn_type< type_of(tuple) >::type_helper(obj, &decay<decltype(obj)>::type::fname) ), \ &decay<decltype(obj)>::type::fname \ > \ (tuple);
Dan penggunaan:
struct DelayedCall { void call_me(int a, int b, int c){ std::cout << a+b+c; } void fire(){ tuple<int,int,int> list = make_tuple(1,2,3); apply_tuple(*this, call_me, list); // even simpler than previous implementations } };
Bukti http://goo.gl/5UqVnC inlinable
Dengan perubahan kecil, kita dapat "membebani"
apply_tuple
:#define VA_NARGS_IMPL(_1, _2, _3, _4, _5, _6, _7, _8, N, ...) N #define VA_NARGS(...) VA_NARGS_IMPL(X,##__VA_ARGS__, 7, 6, 5, 4, 3, 2, 1, 0) #define VARARG_IMPL_(base, count, ...) base##count(__VA_ARGS__) #define VARARG_IMPL(base, count, ...) VARARG_IMPL_(base, count, __VA_ARGS__) #define VARARG(base, ...) VARARG_IMPL(base, VA_NARGS(__VA_ARGS__), __VA_ARGS__) #define apply_tuple2(fname, tuple) apply_tuple3(*this, fname, tuple) #define apply_tuple3(obj, fname, tuple) \ APPLY_TUPLE<typename decay<decltype(obj)>::type, typename decay<decltype(tuple)>::type >(obj).delayed_call< \ decltype( fn_type< type_of(tuple) >::type_helper(obj, &decay<decltype(obj)>::type::fname) ), \ &decay<decltype(obj)>::type::fname \ /* ,decltype(tuple) */> \ (tuple); #define apply_tuple(...) VARARG(apply_tuple, __VA_ARGS__) ... apply_tuple(obj, call_me, list); apply_tuple(call_me, list); // call this->call_me(list....)
Ditambah ini adalah satu-satunya solusi yang berfungsi dengan fungsi template.
sumber
1) jika Anda memiliki struktur parameter_pack readymade sebagai argumen fungsi, Anda dapat menggunakan std :: tie seperti ini:
template <class... Args> void tie_func(std::tuple<Args...> t, Args&... args) { std::tie<Args...>(args...) = t; } int main() { std::tuple<int, double, std::string> t(2, 3.3, "abc"); int i; double d; std::string s; tie_func(t, i, d, s); std::cout << i << " " << d << " " << s << std::endl; }
2) jika Anda tidak memiliki argumen parampack yang sudah jadi, Anda harus melepaskan tupel seperti ini
#include <tuple> #include <functional> #include <iostream> template<int N> struct apply_wrap { template<typename R, typename... TupleArgs, typename... UnpackedArgs> static R applyTuple( std::function<R(TupleArgs...)>& f, const std::tuple<TupleArgs...>& t, UnpackedArgs... args ) { return apply_wrap<N-1>::applyTuple( f, t, std::get<N-1>( t ), args... ); } }; template<> struct apply_wrap<0> { template<typename R, typename... TupleArgs, typename... UnpackedArgs> static R applyTuple( std::function<R(TupleArgs...)>& f, const std::tuple<TupleArgs...>&, UnpackedArgs... args ) { return f( args... ); } }; template<typename R, typename... TupleArgs> R applyTuple( std::function<R(TupleArgs...)>& f, std::tuple<TupleArgs...> const& t ) { return apply_wrap<sizeof...(TupleArgs)>::applyTuple( f, t ); } int fac(int n) { int r=1; for(int i=2; i<=n; ++i) r *= i; return r; } int main() { auto t = std::make_tuple(5); auto f = std::function<decltype(fac)>(&fac); cout << applyTuple(f, t); }
sumber
Bagaimana dengan ini:
// Warning: NOT tested! #include <cstddef> #include <tuple> #include <type_traits> #include <utility> using std::declval; using std::forward; using std::get; using std::integral_constant; using std::size_t; using std::tuple; namespace detail { template < typename Func, typename ...T, typename ...Args > auto explode_tuple( integral_constant<size_t, 0u>, tuple<T...> const &t, Func &&f, Args &&...a ) -> decltype( forward<Func>(f)(declval<T const>()...) ) { return forward<Func>( f )( forward<Args>(a)... ); } template < size_t Index, typename Func, typename ...T, typename ...Args > auto explode_tuple( integral_constant<size_t, Index>, tuple<T...> const&t, Func &&f, Args &&...a ) -> decltype( forward<Func>(f)(declval<T const>()...) ) { return explode_tuple( integral_constant<size_t, Index - 1u>{}, t, forward<Func>(f), get<Index - 1u>(t), forward<Args>(a)... ); } } template < typename Func, typename ...T > auto run_tuple( Func &&f, tuple<T...> const &t ) -> decltype( forward<Func>(f)(declval<T const>()...) ) { return detail::explode_tuple( integral_constant<size_t, sizeof...(T)>{}, t, forward<Func>(f) ); } template < typename Tret, typename ...T > Tret func_T( tuple<T...> const &t ) { return run_tuple( &func<Tret, T...>, t ); }
The
run_tuple
fungsi template mengambil tupel yang diberikan dan lulus elemen individual untuk fungsi yang diberikan. Ia menjalankan tugasnya dengan memanggil template fungsi helpernya secara rekursifexplode_tuple
. Penting untukrun_tuple
meneruskan ukuran tupel keexplode_tuple
; angka itu bertindak sebagai penghitung berapa banyak elemen yang akan diekstrak.Jika tupel kosong, maka
run_tuple
memanggil versi pertamaexplode_tuple
dengan fungsi jarak jauh sebagai satu-satunya argumen lainnya. Fungsi jarak jauh dipanggil tanpa argumen dan kami selesai. Jika tupel tidak kosong, angka yang lebih tinggi akan diteruskan ke versi keduaexplode_tuple
, bersama dengan fungsi jarak jauh. Panggilan rekursif keexplode_tuple
dibuat, dengan argumen yang sama, kecuali nomor counter dikurangi satu dan (referensi ke) elemen tupel terakhir ditempelkan sebagai argumen setelah fungsi jarak jauh. Dalam panggilan rekursif, baik penghitung bukan nol, dan panggilan lain dibuat dengan penghitung dikurangi lagi dan elemen berikutnya-tidak direferensikan disisipkan dalam daftar argumen setelah fungsi jarak jauh tetapi sebelum argumen yang disisipkan lainnya, atau penghitung mencapai nol dan fungsi jarak jauh dipanggil dengan semua argumen yang dikumpulkan setelahnya.Saya tidak yakin saya memiliki sintaks untuk memaksa versi tertentu dari template fungsi dengan benar. Saya pikir Anda dapat menggunakan pointer-to-function sebagai objek fungsi; kompiler akan secara otomatis memperbaikinya.
sumber
Saya sedang mengevaluasi MSVS 2013RC, dan gagal mengumpulkan beberapa solusi sebelumnya yang diusulkan di sini dalam beberapa kasus. Misalnya, MSVS akan gagal untuk mengkompilasi pengembalian "otomatis" jika ada terlalu banyak parameter fungsi, karena batas imbrication namespace (saya mengirim info itu ke Microsoft untuk memperbaikinya). Dalam kasus lain, kita memerlukan akses ke fungsi yang dikembalikan, meskipun itu juga dapat dilakukan dengan lamda: dua contoh berikut memberikan hasil yang sama ..
apply_tuple([&ret1](double a){ret1 = cos(a); }, std::make_tuple<double>(.2)); ret2 = apply_tuple((double(*)(double))cos, std::make_tuple<double>(.2));
Dan sekali lagi terima kasih kepada mereka yang memposting jawaban di sini sebelum saya, saya tidak akan mendapatkan ini tanpanya ... jadi ini dia:
template<size_t N> struct apply_impl { template<typename F, typename T, typename... A> static inline auto apply_tuple(F&& f, T&& t, A&&... a) -> decltype(apply_impl<N-1>::apply_tuple(std::forward<F>(f), std::forward<T>(t), std::get<N-1>(std::forward<T>(t)), std::forward<A>(a)...)) { return apply_impl<N-1>::apply_tuple(std::forward<F>(f), std::forward<T>(t), std::get<N-1>(std::forward<T>(t)), std::forward<A>(a)...); } template<typename C, typename F, typename T, typename... A> static inline auto apply_tuple(C*const o, F&& f, T&& t, A&&... a) -> decltype(apply_impl<N-1>::apply_tuple(o, std::forward<F>(f), std::forward<T>(t), std::get<N-1>(std::forward<T>(t)), std::forward<A>(a)...)) { return apply_impl<N-1>::apply_tuple(o, std::forward<F>(f), std::forward<T>(t), std::get<N-1>(std::forward<T>(t)), std::forward<A>(a)...); } }; // This is a work-around for MSVS 2013RC that is required in some cases #if _MSC_VER <= 1800 /* update this when bug is corrected */ template<> struct apply_impl<6> { template<typename F, typename T, typename... A> static inline auto apply_tuple(F&& f, T&& t, A&&... a) -> decltype(std::forward<F>(f)(std::get<0>(std::forward<T>(t)), std::get<1>(std::forward<T>(t)), std::get<2>(std::forward<T>(t)), std::get<3>(std::forward<T>(t)), std::get<4>(std::forward<T>(t)), std::get<5>(std::forward<T>(t)), std::forward<A>(a)...)) { return std::forward<F>(f)(std::get<0>(std::forward<T>(t)), std::get<1>(std::forward<T>(t)), std::get<2>(std::forward<T>(t)), std::get<3>(std::forward<T>(t)), std::get<4>(std::forward<T>(t)), std::get<5>(std::forward<T>(t)), std::forward<A>(a)...); } template<typename C, typename F, typename T, typename... A> static inline auto apply_tuple(C*const o, F&& f, T&& t, A&&... a) -> decltype((o->*std::forward<F>(f))(std::get<0>(std::forward<T>(t)), std::get<1>(std::forward<T>(t)), std::get<2>(std::forward<T>(t)), std::get<3>(std::forward<T>(t)), std::get<4>(std::forward<T>(t)), std::get<5>(std::forward<T>(t)), std::forward<A>(a)...)) { return (o->*std::forward<F>(f))(std::get<0>(std::forward<T>(t)), std::get<1>(std::forward<T>(t)), std::get<2>(std::forward<T>(t)), std::get<3>(std::forward<T>(t)), std::get<4>(std::forward<T>(t)), std::get<5>(std::forward<T>(t)), std::forward<A>(a)...); } }; #endif template<> struct apply_impl<0> { template<typename F, typename T, typename... A> static inline auto apply_tuple(F&& f, T&&, A&&... a) -> decltype(std::forward<F>(f)(std::forward<A>(a)...)) { return std::forward<F>(f)(std::forward<A>(a)...); } template<typename C, typename F, typename T, typename... A> static inline auto apply_tuple(C*const o, F&& f, T&&, A&&... a) -> decltype((o->*std::forward<F>(f))(std::forward<A>(a)...)) { return (o->*std::forward<F>(f))(std::forward<A>(a)...); } }; // Apply tuple parameters on a non-member or static-member function by perfect forwarding template<typename F, typename T> inline auto apply_tuple(F&& f, T&& t) -> decltype(apply_impl<std::tuple_size<typename std::decay<T>::type>::value>::apply_tuple(std::forward<F>(f), std::forward<T>(t))) { return apply_impl<std::tuple_size<typename std::decay<T>::type>::value>::apply_tuple(std::forward<F>(f), std::forward<T>(t)); } // Apply tuple parameters on a member function template<typename C, typename F, typename T> inline auto apply_tuple(C*const o, F&& f, T&& t) -> decltype(apply_impl<std::tuple_size<typename std::decay<T>::type>::value>::apply_tuple(o, std::forward<F>(f), std::forward<T>(t))) { return apply_impl<std::tuple_size<typename std::decay<T>::type>::value>::apply_tuple(o, std::forward<F>(f), std::forward<T>(t)); }
sumber
const
?Memperluas solusi @ David, Anda dapat menulis template rekursif itu
integer_sequence
semantik (terlalu bertele-tele, imo)int N
untuk menghitung iterasi rekursifMisalnya:
template <class F, F func> struct static_functor { template <class... T, class... Args_tmp> static inline auto apply(const std::tuple<T...>& t, Args_tmp... args) -> decltype(func(std::declval<T>()...)) { return static_functor<F,func>::apply(t, args..., std::get<sizeof...(Args_tmp)>(t)); } template <class... T> static inline auto apply(const std::tuple<T...>& t, T... args) -> decltype(func(args...)) { return func(args...); } }; static_functor<decltype(&myFunc), &myFunc>::apply(my_tuple);
Alternatifnya jika functor Anda tidak ditentukan pada waktu kompilasi (misalnya,
constexpr
turunan non- functor, atau ekspresi lambda), Anda dapat menggunakannya sebagai parameter fungsi alih-alih parameter templat kelas, dan pada kenyataannya menghapus seluruh kelas yang memuatnya:template <class F, class... T, class... Args_tmp> inline auto apply_functor(F&& func, const std::tuple<T...>& t, Args_tmp... args) -> decltype(func(std::declval<T>()...)) { return apply_functor(func, t, args..., std::get<sizeof...(Args_tmp)>(t)); } template <class F, class... T> inline auto apply_functor(F&& func, const std::tuple<T...>& t, T... args) -> decltype(func(args...)) { return func(args...); } apply_functor(&myFunc, my_tuple);
Untuk callable pointer-to-member-function, Anda dapat menyesuaikan salah satu dari potongan kode di atas sama seperti dalam jawaban @ David.
Penjelasan
Mengacu pada potongan kode kedua, ada dua fungsi template: yang pertama mengambil functor
func
, tuplet
dengan tipeT...
, dan paket parameterargs
tipeArgs_tmp...
. Ketika dipanggil, itu secara rekursif menambahkan objek darit
ke paket parameter satu per satu, dari awal (0
) ke akhir, dan memanggil fungsi lagi dengan paket parameter incremented baru.Tanda tangan fungsi kedua hampir identik dengan yang pertama, kecuali fungsi ini menggunakan tipe
T...
untuk paket parameterargs
. Jadi, setelahargs
fungsi pertama terisi penuh dengan nilai darit
, tipenya akan menjadiT...
(dalam psuedo-code,typeid(T...) == typeid(Args_tmp...)
), dan dengan demikian compiler akan memanggil fungsi kedua yang kelebihan beban, yang pada gilirannya akan memanggilfunc(args...)
.Kode dalam contoh fungsi statis bekerja sama, dengan functor sebagai gantinya digunakan sebagai argumen template kelas.
sumber
Mengapa tidak hanya membungkus argumen variadic Anda ke dalam kelas tuple dan kemudian menggunakan rekursi waktu kompilasi (lihat tautan ) untuk mengambil indeks yang Anda minati. Saya menemukan bahwa membongkar template variadic ke dalam wadah atau koleksi mungkin bukan tipe aman wrt tipe heterogen
template<typename... Args> auto get_args_as_tuple(Args... args) -> std::tuple<Args...> { return std::make_tuple(args); }
sumber
Args...
->tuple
, tapituple
->Args...
.Solusi sederhana ini berhasil untuk saya:
template<typename... T> void unwrap_tuple(std::tuple<T...>* tp) { std::cout << "And here I have the tuple types, all " << sizeof...(T) << " of them" << std::endl; } int main() { using TupleType = std::tuple<int, float, std::string, void*>; unwrap_tuple((TupleType*)nullptr); // trick compiler into using template param deduction }
sumber