Nilai penutupan Lambda dapat dilewatkan sebagai parameter referensi nilai ulang


18

Saya menemukan bahwa lvaluepenutupan lambda selalu dapat dilewati sebagai rvalueparameter fungsi.

Lihat demonstrasi sederhana berikut.

#include <iostream>
#include <functional>

using namespace std;

void foo(std::function<void()>&& t)
{
}

int main()
{
    // Case 1: passing a `lvalue` closure
    auto fn1 = []{};
    foo(fn1);                          // works

    // Case 2: passing a `lvalue` function object
    std::function<void()> fn2 = []{};
    foo(fn2);                          // compile error

    return 0;
}

Kasus 2 adalah perilaku standar (saya hanya menggunakan std::functionuntuk tujuan demonstrasi, tetapi jenis lainnya akan berperilaku sama).

Bagaimana dan mengapa kasus 1 berfungsi? Bagaimana keadaan fn1penutupan setelah fungsi kembali?


5
Saya kira itu karena fn1secara implisit dikonversi ke std::functiondalam foo(fn1). Fungsi sementara itu adalah nilai.
eike

@RichardCritten Saya benar-benar tidak yakin, jadi saya tidak memposting jawaban. Saya pikir sekarang tidak perlu yang lain.
eike

1
@ ee np Saya sering merasakan hal yang sama, dan ya banyak jawaban.
Richard Critten

2
@Sumudu Orang yang mengajukan pertanyaan itu menyesatkan Anda karena mereka tidak tahu apa yang mereka coba tanyakan. Apa yang ingin mereka tanyakan adalah: "Mengapa tidak bisa membuat templat argumen yang std::functiondiambil dari lambda". Program Anda tidak berupaya menyimpulkan argumen templat std::function, jadi tidak ada masalah dengan konversi implisit.
eerorika

1
Judul pertanyaan yang Anda tautkan sedikit menyesatkan. std::functionmemiliki konstruktor non-eksplisit yang menerima penutupan lambda, sehingga ada konversi implisit. Tetapi dalam keadaan pertanyaan terkait, instantiasi template std::functiontidak dapat disimpulkan dari jenis lambda. (Misalnya std::function<void()>dapat dibangun dari [](){return 5;}meskipun memiliki jenis kembali tidak batal.
eike

Jawaban:


8

Bagaimana dan mengapa kasus 1 berfungsi?

Meminta foomembutuhkan instance std::function<void()>yang mengikat referensi nilai . std::function<void()>dapat dibangun dari objek yang dapat dipanggil yang kompatibel dengan void()tanda tangan.

Pertama, std::function<void()>objek sementara dibangun dari []{}. Konstruktor yang digunakan adalah # 5 di sini , yang menyalin penutupan ke std::functioninstance:

template< class F >
function( F f );

Menginisialisasi target dengan std::move(f). Jika fnull pointer berfungsi atau null pointer ke anggota, *thisakan kosong setelah panggilan.

Kemudian, functioninstance sementara terikat pada referensi nilai.


Apa status penutupan fn1 setelah fungsi kembali?

Sama seperti sebelumnya, karena disalin ke std::functioninstance. Penutupan asli tidak terpengaruh.


8

Lambda bukan a std::function. Referensi tidak mengikat secara langsung .

Kasus 1 berfungsi karena lambdas dapat dikonversi menjadi std::functions. Ini berarti bahwa suatu sementara std::functiondiwujudkan dengan menyalin fn1 . Kata sementara dapat diikat ke referensi nilai, dan argumen cocok dengan parameter.

Dan penyalinan juga mengapa fn1sama sekali tidak terpengaruh oleh apa pun yang terjadi di foo.


5

Apa status penutupan fn1 setelah fungsi kembali?

fn1 tidak memiliki kewarganegaraan, karena tidak menangkap apa pun.

Bagaimana dan mengapa kasus 1 berfungsi?

Ini berfungsi karena argumennya bertipe berbeda dari jenis yang dirujuk. Karena memiliki tipe yang berbeda, konversi implisit dipertimbangkan. Karena lambda adalah Callable untuk argumen ini std::function, maka secara implisit dapat dikonversi untuk itu melalui template yang mengkonversi konstruktor std::function. Hasil konversi adalah nilai awal, dan dengan demikian dapat diikat dengan referensi nilai.

Dengan menggunakan situs kami, Anda mengakui telah membaca dan memahami Kebijakan Cookie dan Kebijakan Privasi kami.
Licensed under cc by-sa 3.0 with attribution required.