Saya mencari aturan yang melibatkan fungsi templat C ++ sebagai argumen.
Ini didukung oleh C ++ seperti yang ditunjukkan oleh contoh di sini:
#include <iostream>
void add1(int &v)
{
v+=1;
}
void add2(int &v)
{
v+=2;
}
template <void (*T)(int &)>
void doOperation()
{
int temp=0;
T(temp);
std::cout << "Result is " << temp << std::endl;
}
int main()
{
doOperation<add1>();
doOperation<add2>();
}
Namun, mempelajari teknik ini sulit. Googling untuk "berfungsi sebagai argumen templat" tidak menghasilkan banyak. Dan Template C ++ klasik Panduan Lengkap mengejutkan juga tidak membahasnya (setidaknya bukan dari pencarian saya).
Pertanyaan saya adalah apakah ini valid C ++ (atau hanya beberapa ekstensi yang didukung secara luas).
Juga, apakah ada cara untuk memungkinkan functor dengan tanda tangan yang sama untuk digunakan secara bergantian dengan fungsi eksplisit selama jenis permohonan template ini?
Berikut ini tidak berfungsi dalam program di atas, setidaknya dalam Visual C ++ , karena sintaksnya jelas salah. Akan lebih baik untuk dapat beralih fungsi untuk functor dan sebaliknya, mirip dengan cara Anda dapat melewati pointer fungsi atau functor ke std :: sort algoritma jika Anda ingin mendefinisikan operasi perbandingan kustom.
struct add3 {
void operator() (int &v) {v+=3;}
};
...
doOperation<add3>();
Pointer ke satu atau dua tautan web, atau satu halaman di buku C ++ Templates akan dihargai!
sumber
-std=gnu++17
. Dapatkah saya menggunakan hasil dari operator konversi constexpr lambda C ++ 17 captureless sebagai argumen penunjuk fungsi templat-tipe? .Jawaban:
Ya itu benar.
Untuk membuatnya bekerja dengan functors juga, solusi yang biasa adalah sesuatu seperti ini sebagai gantinya:
yang sekarang dapat disebut sebagai:
Lihat itu langsung
Masalah dengan ini adalah bahwa jika itu membuat rumit bagi kompiler untuk inline panggilan ke
add2
, karena semua kompiler tahu adalah bahwa jenis fungsi pointervoid (*)(int &)
sedang diteruskan kedoOperation
. (Tetapiadd3
, sebagai functor, dapat diuraikan dengan mudah. Di sini, kompiler tahu bahwa objek bertipeadd3
dilewatkan ke fungsi, yang berarti bahwa fungsi yang dipanggiladd3::operator()
, dan bukan hanya beberapa pointer fungsi yang tidak diketahui.)sumber
template <typename F> void doOperation(F&& f) {/**/}
), jadi bind misalnya dapat melewatkan ekspresi bind daripada mengikatnya?Parameter templat dapat berupa parameter berdasarkan jenis (jenis nama T) atau menurut nilai (int X).
The "tradisional" C ++ cara templating sepotong kode adalah dengan menggunakan functor - yaitu, kode itu dalam suatu objek, dan objek dengan demikian memberikan jenis kode yang unik.
Ketika bekerja dengan fungsi tradisional, teknik ini tidak berfungsi dengan baik, karena perubahan tipe tidak menunjukkan fungsi tertentu - melainkan hanya menentukan tanda tangan dari banyak fungsi yang mungkin. Begitu:
Tidak setara dengan kasing functor. Dalam contoh ini, do_op dipakai untuk semua pointer fungsi yang tanda tangannya adalah int X (int, int). Kompiler harus cukup agresif untuk sepenuhnya menyelaraskan kasus ini. (Saya tidak akan mengesampingkannya, karena optimasi kompiler sudah cukup maju.)
Salah satu cara untuk mengatakan bahwa kode ini tidak cukup melakukan apa yang kita inginkan adalah:
masih legal, dan jelas ini tidak mendapatkan inline. Untuk mendapatkan inlining penuh, kita perlu membuat templat berdasarkan nilai, sehingga fungsi tersebut sepenuhnya tersedia dalam templat.
Dalam hal ini, setiap versi do_op yang dipakai akan dipakai dengan fungsi tertentu yang sudah tersedia. Jadi kita mengharapkan kode do_op terlihat seperti "mengembalikan + b". (Pemrogram yang lalai, hentikan seringai Anda!)
Kami juga dapat mengkonfirmasi bahwa ini lebih dekat dengan yang kami inginkan karena ini:
akan gagal dikompilasi. GCC mengatakan: "error: 'func_ptr' tidak dapat muncul dalam ekspresi konstan. Dengan kata lain, saya tidak dapat sepenuhnya memperluas do_op karena Anda belum memberi saya cukup info pada waktu kompiler untuk mengetahui apa op kita.
Jadi, jika contoh kedua benar-benar inlining sepenuhnya op kami, dan yang pertama tidak, apa gunanya template? Apa yang sedang dilakukannya? Jawabannya adalah: ketik paksaan. Riff ini pada contoh pertama akan berfungsi:
Contoh itu akan berhasil! (Saya tidak menyarankan itu baik C ++ tapi ...) Apa yang terjadi adalah do_op telah templated sekitar tanda tangan dari berbagai fungsi, dan setiap instansiasi terpisah akan menulis kode paksaan jenis yang berbeda. Jadi kode instantiated untuk do_op dengan fadd terlihat seperti:
Sebagai perbandingan, case-by-value kami membutuhkan kecocokan yang tepat pada argumen fungsi.
sumber
int c = do_op(4,5,func_ptr);
"jelas tidak mendapatkan inline".Pointer fungsi dapat dilewatkan sebagai parameter templat, dan ini adalah bagian dari standar C ++ . Namun dalam template mereka dideklarasikan dan digunakan sebagai fungsi daripada pointer-ke-fungsi. Pada contoh instantiation satu melewati alamat fungsi bukan hanya nama.
Sebagai contoh:
Jika Anda ingin meneruskan jenis functor sebagai argumen templat:
Beberapa jawaban melewati instance functor sebagai argumen:
Yang paling dekat dengan penampilan seragam ini dengan argumen templat adalah mendefinisikan
do_op
dua kali dengan parameter non-tipe dan sekali dengan parameter tipe.Jujur, saya benar-benar berharap ini tidak dapat dikompilasi, tetapi itu bekerja untuk saya dengan gcc-4.8 dan Visual Studio 2013.
sumber
Dalam templat Anda
Parameter
T
adalah parameter template non-tipe. Ini berarti bahwa perilaku fungsi template berubah dengan nilai parameter (yang harus diperbaiki pada waktu kompilasi, yang merupakan konstanta penunjuk fungsi).Jika Anda menginginkan sesuatu yang berfungsi dengan objek fungsi dan parameter fungsi, Anda memerlukan templat yang diketik. Ketika Anda melakukan ini, Anda juga perlu memberikan instance objek (baik instance function object atau function pointer) ke fungsi pada saat run time.
Ada beberapa pertimbangan kinerja kecil. Versi baru ini mungkin kurang efisien dengan argumen penunjuk fungsi karena penunjuk fungsi tertentu hanya didefinisikan dan dipanggil pada saat run time sedangkan templat penunjuk fungsi Anda dapat dioptimalkan (mungkin pemanggilan fungsi inline) berdasarkan pada penunjuk fungsi tertentu yang digunakan. Objek fungsi sering kali dapat diperluas secara efisien dengan templat yang diketik, meskipun seperti yang
operator()
ditentukan sepenuhnya oleh jenis objek fungsi.sumber
Alasan contoh functor Anda tidak berfungsi adalah karena Anda memerlukan sebuah instance untuk memanggil
operator()
.sumber
Sunting: Melewati operator sebagai referensi tidak berfungsi. Untuk mempermudah, pahami sebagai fungsi penunjuk. Anda hanya mengirim pointer, bukan referensi. Saya pikir Anda mencoba menulis sesuatu seperti ini.
. .
sumber