Apakah mungkin untuk menyimpan paket parameter untuk digunakan nanti?
template <typename... T>
class Action {
private:
std::function<void(T...)> f;
T... args; // <--- something like this
public:
Action(std::function<void(T...)> f, T... args) : f(f), args(args) {}
void act(){
f(args); // <--- such that this will be possible
}
}
Kemudian nanti:
void main(){
Action<int,int> add([](int x, int y){std::cout << (x+y);}, 3, 4);
//...
add.act();
}
c++
c++11
variadic-templates
Eric B
sumber
sumber
Jawaban:
Untuk mencapai apa yang ingin Anda lakukan di sini, Anda harus menyimpan argumen template Anda dalam tupel:
std::tuple<Ts...> args;
Selanjutnya, Anda harus sedikit mengubah konstruktor Anda. Secara khusus, menginisialisasi
args
denganstd::make_tuple
dan juga mengizinkan referensi universal dalam daftar parameter Anda:template <typename F, typename... Args> Action(F&& func, Args&&... args) : f(std::forward<F>(func)), args(std::forward<Args>(args)...) {}
Selain itu, Anda harus menyiapkan generator urutan seperti ini:
namespace helper { template <int... Is> struct index {}; template <int N, int... Is> struct gen_seq : gen_seq<N - 1, N - 1, Is...> {}; template <int... Is> struct gen_seq<0, Is...> : index<Is...> {}; }
Dan Anda dapat mengimplementasikan metode Anda dalam hal menggunakan generator seperti itu:
template <typename... Args, int... Is> void func(std::tuple<Args...>& tup, helper::index<Is...>) { f(std::get<Is>(tup)...); } template <typename... Args> void func(std::tuple<Args...>& tup) { func(tup, helper::gen_seq<sizeof...(Args)>{}); } void act() { func(args); }
Dan itu dia! Jadi sekarang kelas Anda akan terlihat seperti ini:
template <typename... Ts> class Action { private: std::function<void (Ts...)> f; std::tuple<Ts...> args; public: template <typename F, typename... Args> Action(F&& func, Args&&... args) : f(std::forward<F>(func)), args(std::forward<Args>(args)...) {} template <typename... Args, int... Is> void func(std::tuple<Args...>& tup, helper::index<Is...>) { f(std::get<Is>(tup)...); } template <typename... Args> void func(std::tuple<Args...>& tup) { func(tup, helper::gen_seq<sizeof...(Args)>{}); } void act() { func(args); } };
Ini adalah program lengkap Anda di Coliru.
Update: Berikut adalah metode pembantu yang tidak memerlukan spesifikasi argumen template:
template <typename F, typename... Args> Action<Args...> make_action(F&& f, Args&&... args) { return Action<Args...>(std::forward<F>(f), std::forward<Args>(args)...); } int main() { auto add = make_action([] (int a, int b) { std::cout << a + b; }, 2, 3); add.act(); }
Dan lagi, ini demo lainnya.
sumber
void print(const std::string&); std::string hello(); auto act = make_action(print, hello());
tidak bagus. Saya lebih suka perilaku daristd::bind
, yang membuat salinan dari setiap argumen kecuali Anda menonaktifkannya denganstd::ref
ataustd::cref
.args(std::make_tuple(std::forward<Args>(args)...))
keargs(std::forward<Args>(args)...)
. BTW Saya menulis ini sejak lama dan saya tidak akan menggunakan kode ini untuk tujuan mengikat fungsi ke beberapa argumen. Saya hanya akan menggunakanstd::invoke()
ataustd::apply()
saat ini.Anda bisa menggunakan
std::bind(f,args...)
untuk ini. Ini akan menghasilkan objek yang dapat dipindahkan dan mungkin dapat disalin yang menyimpan salinan objek fungsi dan masing-masing argumen untuk digunakan nanti:#include <iostream> #include <utility> #include <functional> template <typename... T> class Action { public: using bind_type = decltype(std::bind(std::declval<std::function<void(T...)>>(),std::declval<T>()...)); template <typename... ConstrT> Action(std::function<void(T...)> f, ConstrT&&... args) : bind_(f,std::forward<ConstrT>(args)...) { } void act() { bind_(); } private: bind_type bind_; }; int main() { Action<int,int> add([](int x, int y) { std::cout << (x+y) << std::endl; }, 3, 4); add.act(); return 0; }
Perhatikan bahwa itu
std::bind
adalah fungsi dan Anda perlu menyimpan, sebagai anggota data, hasil pemanggilannya. Tipe data dari hasil tersebut tidak mudah untuk diprediksi (Standar bahkan tidak menentukannya secara tepat), jadi saya menggunakan kombinasi daridecltype
danstd::declval
untuk menghitung tipe data tersebut pada waktu kompilasi. Lihat definisi diAction::bind_type
atas.Juga perhatikan bagaimana saya menggunakan referensi universal di konstruktor template. Ini memastikan bahwa Anda dapat meneruskan argumen yang tidak sama
T...
persis dengan parameter template kelas (misalnya, Anda dapat menggunakan referensi rvalue ke beberapaT
dan Anda akan meneruskannya apa adanya kebind
panggilan.)Catatan terakhir: Jika Anda ingin menyimpan argumen sebagai referensi (sehingga fungsi yang Anda berikan dapat memodifikasi, daripada hanya menggunakannya, argumen tersebut), Anda perlu menggunakannya
std::ref
untuk menggabungkannya dalam objek referensi. Meneruskan a sajaT &
akan membuat salinan nilai, bukan referensi.Kode operasional di Coliru
sumber
add
didefinisikan dalam lingkup yang berbeda lalu di manaact()
dipanggil? Tidak harus konstruktor mendapatkanConstrT&... args
bukanConstrT&&... args
?bind()
? Karenabind()
dijamin akan membuat salinan (atau pindah ke objek yang baru dibuat), saya rasa tidak akan ada masalah.bind_(f, std::forward<ConstrT>(args)...)
adalah perilaku tidak terdefinisi sesuai dengan standar, karena konstruktor tersebut ditentukan oleh implementasi.bind_type
ditentukan untuk disalin dan / atau dapat dipindahkan, jadibind_{std::bind(f, std::forward<ConstrT>(args)...)}
harus tetap berfungsi.Pertanyaan ini berasal dari C ++ 11 hari. Tetapi bagi mereka yang menemukannya di hasil pencarian sekarang, beberapa pembaruan:
Seorang
std::tuple
anggota masih merupakan cara yang mudah untuk menyimpan argumen secara umum. (std::bind
Solusi yang mirip dengan @ jogojapan juga akan berfungsi jika Anda hanya ingin memanggil fungsi tertentu, tetapi tidak jika Anda ingin mengakses argumen dengan cara lain, atau meneruskan argumen ke lebih dari satu fungsi, dll.)Di C ++ 14 dan yang lebih baru,
std::make_index_sequence<N>
ataustd::index_sequence_for<Pack...>
dapat menggantikanhelper::gen_seq<N>
alat yang terlihat di solusi 0x499602D2 :#include <utility> template <typename... Ts> class Action { // ... template <typename... Args, std::size_t... Is> void func(std::tuple<Args...>& tup, std::index_sequence<Is...>) { f(std::get<Is>(tup)...); } template <typename... Args> void func(std::tuple<Args...>& tup) { func(tup, std::index_sequence_for<Args...>{}); } // ... };
Di C ++ 17 dan yang lebih baru,
std::apply
dapat digunakan untuk menangani pembongkaran tupel:template <typename... Ts> class Action { // ... void act() { std::apply(f, args); } };
Berikut adalah program C ++ 17 lengkap yang menunjukkan implementasi yang disederhanakan. Saya juga memperbarui
make_action
untuk menghindari jenis referensi dituple
, yang selalu buruk untuk argumen nilai r dan cukup berisiko untuk argumen nilai l.sumber
Saya pikir Anda memiliki masalah XY. Mengapa repot-repot menyimpan paket parameter ketika Anda bisa saja menggunakan lambda di situs panggilan? yaitu,
#include <functional> #include <iostream> typedef std::function<void()> Action; void callback(int n, const char* s) { std::cout << s << ": " << n << '\n'; } int main() { Action a{[]{callback(13, "foo");}}; a(); }
sumber