Katakanlah saya memiliki fungsi yang membutuhkan std::function
:
void callFunction(std::function<void()> x)
{
x();
}
Haruskah saya melewati x
referensi const sebagai gantinya ?:
void callFunction(const std::function<void()>& x)
{
x();
}
Apakah jawaban atas pertanyaan ini berubah tergantung pada apa fungsinya dengannya? Misalnya jika itu adalah fungsi anggota kelas atau konstruktor yang menyimpan atau menginisialisasi std::function
menjadi variabel anggota.
sizeof(std::function)
tidak lebih dari2 * sizeof(size_t)
, yang merupakan ukuran terkecil yang pernah Anda pertimbangkan untuk referensi const.std::function
pembungkusnya tidak sepenting kerumitan menyalinnya. Jika deep copy dilibatkan, harganya bisa jauh lebih mahal daripada yangsizeof
disarankan.move
menjalankan fungsinya?operator()()
adalahconst
begitu referensi const harus bekerja. Tapi saya tidak pernah menggunakan std :: function.Jawaban:
Jika Anda menginginkan kinerja, berikan nilai jika Anda menyimpannya.
Misalkan Anda memiliki fungsi yang disebut "jalankan ini di thread UI".
std::future<void> run_in_ui_thread( std::function<void()> )
yang menjalankan beberapa kode di utas "ui", lalu memberi sinyal
future
saat selesai. (Berguna dalam kerangka UI di mana utas UI adalah tempat Anda seharusnya mengacaukan elemen UI)Kami memiliki dua tanda tangan yang sedang kami pertimbangkan:
std::future<void> run_in_ui_thread( std::function<void()> ) // (A) std::future<void> run_in_ui_thread( std::function<void()> const& ) // (B)
Sekarang, kami cenderung menggunakan ini sebagai berikut:
run_in_ui_thread( [=]{ // code goes here } ).wait();
yang akan membuat penutupan anonim (lambda), membuat
std::function
keluar darinya, meneruskannya kerun_in_ui_thread
fungsi, lalu menunggu hingga selesai berjalan di utas utama.Dalam kasus (A),
std::function
secara langsung dibangun dari lambda kami, yang kemudian digunakan dalam filerun_in_ui_thread
. Lambda adalahmove
d ke dalamstd::function
, sehingga setiap status bergerak secara efisien dibawa ke dalamnya.Dalam kasus kedua, sementara
std::function
dibuat, lambdamove
d ke dalamnya, kemudian sementarastd::function
itu digunakan sebagai referensi dalam filerun_in_ui_thread
.Sejauh ini, sangat bagus - keduanya tampil identik. Kecuali
run_in_ui_thread
akan membuat salinan argumen fungsinya untuk dikirim ke utas ui untuk dieksekusi! (itu akan kembali sebelum selesai dengannya, jadi tidak bisa hanya menggunakan referensi ke sana). Untuk kasus (A), kami hanyamove
memasukkanstd::function
ke dalam penyimpanan jangka panjangnya. Dalam kasus (B), kami dipaksa untuk menyalin filestd::function
.Penyimpanan itu membuat nilai lewat lebih optimal. Jika ada kemungkinan Anda menyimpan salinan dari nilai
std::function
lewat. Jika tidak, cara mana pun secara kasar setara: satu-satunya sisi negatif dari nilai sampingan adalah jika Anda menggunakan ukuran besar yang samastd::function
dan memiliki satu sub metode setelah yang lain menggunakannya. Kecuali itu, amove
akan seefisien aconst&
.Sekarang, ada beberapa perbedaan lain antara keduanya yang kebanyakan muncul jika kita memiliki status persisten di dalam
std::function
.Asumsikan bahwa
std::function
menyimpan beberapa objek dengan aoperator() const
, tetapi juga memiliki beberapamutable
anggota data yang dimodifikasi (betapa kasarnya!).Dalam
std::function<> const&
kasus ini,mutable
anggota data yang dimodifikasi akan menyebar keluar dari pemanggilan fungsi. Dalamstd::function<>
kasus ini, mereka tidak akan melakukannya.Ini adalah kasus sudut yang relatif aneh.
Anda ingin memperlakukan
std::function
seperti Anda memperlakukan jenis lain yang mungkin berbobot berat dan dapat dipindahkan dengan murah. Pindahan itu murah, menyalin bisa mahal.sumber
const&
saya hanya melihat biaya operasi penyalinan.std::function
dibuat dari lambda. Dalam (A), sementara dielaborasi menjadi argumen untukrun_in_ui_thread
. Dalam (B) referensi ke sementara tersebut diteruskan kerun_in_ui_thread
. Selamastd::function
s Anda dibuat dari lambda sebagai temporer, klausa itu berlaku. Paragraf sebelumnya membahas kasus di mana masihstd::function
ada. Jika kita tidak menyimpan, hanya membuat dari lambda,function const&
danfunction
memiliki overhead yang sama persis.run_in_ui_thread()
. Apakah hanya ada tanda tangan yang mengatakan "Lewati referensi, tetapi saya tidak akan menyimpan alamatnya"?std::future<void> run_in_ui_thread( std::function<void()>&& )
Jika Anda khawatir tentang kinerja, dan Anda tidak menentukan fungsi anggota virtual, kemungkinan besar Anda tidak boleh menggunakan
std::function
sama sekali.Membuat jenis functor sebagai parameter template memungkinkan pengoptimalan yang lebih besar daripada
std::function
, termasuk menyebariskan logika functor. Efek dari pengoptimalan ini kemungkinan besar jauh lebih besar daripada masalah copy-vs-indirection tentang cara mengoperstd::function
.Lebih cepat:
template<typename Functor> void callFunction(Functor&& x) { x(); }
sumber
std::forward<Functor>(x)();
, untuk mempertahankan kategori nilai dari functor, karena ini adalah referensi "universal". Tidak akan membuat perbedaan dalam 99% kasus.callFunction(std::move(myFunctor));
std::move
jika Anda tidak lagi membutuhkannya dengan cara lain, atau meneruskan secara langsung jika Anda tidak ingin keluar dari objek yang ada. Aturan penciutan referensi memastikan bahwacallFunction<T&>()
memiliki parameter bertipeT&
, bukanT&&
.Seperti biasa di C ++ 11, meneruskan value / reference / const-reference bergantung pada apa yang Anda lakukan dengan argumen Anda.
std::function
tidak berbeda.Melewati nilai memungkinkan Anda untuk memindahkan argumen ke dalam variabel (biasanya variabel anggota kelas):
struct Foo { Foo(Object o) : m_o(std::move(o)) {} Object m_o; };
Ketika Anda tahu fungsi Anda akan memindahkan argumennya, ini adalah solusi terbaik, dengan cara ini pengguna Anda dapat mengontrol bagaimana mereka memanggil fungsi Anda:
Foo f1{Object()}; // move the temporary, followed by a move in the constructor Foo f2{some_object}; // copy the object, followed by a move in the constructor Foo f3{std::move(some_object)}; // move the object, followed by a move in the constructor
Saya yakin Anda sudah mengetahui semantik dari (non) referensi konstanta jadi saya tidak akan mempermasalahkannya. Jika Anda ingin saya menambahkan lebih banyak penjelasan tentang ini, tanyakan saja dan saya akan memperbarui.
sumber