Bagaimana lambda generik bekerja di C ++ 14?

114

Bagaimana lambda generik bekerja ( autokata kunci sebagai tipe argumen) dalam standar C ++ 14?

Apakah ini didasarkan pada template C ++ di mana untuk setiap kompilator tipe argumen yang berbeda menghasilkan fungsi baru dengan isi yang sama tetapi tipe yang diganti (polimorfisme waktu kompilasi) atau lebih mirip dengan generik Java (penghapusan tipe)?

Contoh kode:

auto glambda = [](auto a) { return a; };
sasha.sochka
sumber
6
Diperbaiki ke C ++ 14, awalnya menggunakan C ++ 11 yang dimaksud
sasha.sochka

Jawaban:

130

Lambda generik diperkenalkan di C++14.

Sederhananya, tipe closure yang ditentukan oleh ekspresi lambda akan memiliki operator panggilan templated daripada operator panggilan non-template reguler dari lambda C++11(tentu saja, ketika automuncul setidaknya sekali dalam daftar parameter).

Jadi contoh Anda:

auto glambda = [] (auto a) { return a; };

Akan membuat glambdacontoh jenis ini:

class /* unnamed */
{
public:
    template<typename T>
    T operator () (T a) const { return a; }
};

Paragraf 5.1.2 / 5 dari C ++ 14 Standard Draft n3690 menentukan bagaimana operator panggilan dari tipe closure dari ekspresi lambda yang diberikan didefinisikan:

Tipe penutupan untuk ekspresi lambda non-generik memiliki operator panggilan fungsi sebaris publik (13.5.4) yang parameter dan tipe kembaliannya dijelaskan oleh masing-masing parameter-deklarasi-klausa-lambda-ekspresi dan tipe-kembali-jejak. Untuk lambda generik, tipe closure memiliki templat anggota operator panggilan fungsi sebaris publik (14.5.2) yang daftar templat-parameternya terdiri dari satu jenis templat-parameter yang ditemukan untuk setiap kemunculan otomatis dalam klausa-deklarasi-parameter lambda, dalam urutan penampilan. Jenis template-parameter yang ditemukan adalah paket parameter jika deklarasi parameter terkait mendeklarasikan paket parameter fungsi (8.3.5). Jenis kembalian dan parameter fungsi dari template operator panggilan fungsi diturunkan dari klausa trailing-return-type dan parameter-declarationclause dari lambda-expression dengan mengganti setiap kemunculan auto di dec-specifier dari parameter-declaration-clause dengan nama dari parameter template yang ditemukan yang sesuai.

Akhirnya:

Apakah mirip dengan template di mana untuk setiap kompilator tipe argumen yang berbeda menghasilkan fungsi dengan isi yang sama tetapi tipe yang berubah atau lebih mirip dengan generik Java?

Seperti yang dijelaskan paragraf di atas, lambda generik hanyalah gula sintaksis untuk fungsi unik dan tidak bernama dengan operator panggilan templated. Itu harus menjawab pertanyaan Anda :)

Andy Prowl
sumber
7
Namun, mereka juga mengizinkan kelas yang ditentukan secara lokal dengan metode template. Yang baru.
Yakk - Adam Nevraumont
2
@Yakk: Bukankah pembatasan untuk template lokal-fungsi telah dihapus seluruhnya dengan C ++ 11?
Sebastian Mach
2
@phresnel: Tidak, pembatasan itu belum dicabut
Andy Prowl
1
@AndyProwl: Saya menyadari kesalahan saya. Apa yang diangkat memang menggunakan tipe lokal sebagai argumen template (seperti dalam int main () { struct X {}; std::vector<X> x; })
Sebastian Mach
1
@phresnel: Benar, itu memang telah berubah
Andy Prowl
25

Sayangnya , mereka bukan bagian dari C ++ 11 ( http://ideone.com/NsqYuq ):

auto glambda = [](auto a) { return a; };

int main() {}

Dengan g ++ 4.7:

prog.cpp:1:24: error: parameter declared auto
...

Namun , cara penerapannya di C ++ 14 sesuai dengan proposal Portland untuk lambda generik :

[](const& x, & y){ return x + y; }

Ini akan menghasilkan sebagian besar pembuatan biasa dari kelas functor anonim, tetapi dengan ketiadaan tipe kompilator akan memancarkan anggota bertemplat- operator():

struct anonymous
{
    template <typename T, typename U>
    auto operator()(T const& x, U& y) const -> decltype(x+y)
    { return x + y; }
};

Atau sesuai proposal yang lebih baru Proposal for Generic (Polymorphic) Lambda Expressions

auto L = [](const auto& x, auto& y){ return x + y; };

--->

struct /* anonymous */
{
    template <typename T, typename U>
    auto operator()(const T& x, U& y) const // N3386 Return type deduction
    { return x + y; }
} L;

Jadi ya, untuk setiap permutasi parameter, sebuah instansiasi baru akan muncul, namun, anggota dari functor itu akan tetap dibagikan (yaitu argumen yang ditangkap).

Sebastian Mach
sumber
6
Proposal yang memungkinkan penghapusan penentu jenis itu benar-benar aneh.
Balapan Lightness di Orbit
Mereka masuk dengan g ++ - 4.9 . Anda perlu memasok -std=c++1y.
emsr
Saya tidak menyadari ideone belum memiliki gcc-4.9 dan C ++ 14.
emsr
pertanyaan: apakah ini automemiliki aturan pemotongan yang sama dengan mobil klasik? Jika kita mengacu pada analogi templated, itu berarti auto bukan auto, itu adalah aturan yang sama dengan pengurangan jenis template. Lalu pertanyaannya adalah: apakah pemotongan template setara dengan auto?
v.oddou
@ v.oddou: "Klasik otomatis" itu bagus. Bagi saya, "mobil klasik" berarti "Stack Variable", dan pernah digunakan sebagai kontras dengan staticatau register:) Bagaimanapun, ya, menggunakan di autosana berarti bahwa di balik kap mesin, template normal dibuat. Faktanya, lambda akan diganti compiler-internal oleh sebuah kelas functor, dan sebuah autoparameter berarti yang template <T> ... (T ...)akan dikeluarkan.
Sebastian Mach
17

Ini adalah fitur C ++ 14 yang diusulkan (tidak dalam C ++ 11) yang mirip (atau bahkan setara) dengan template. Misalnya, N3559 memberikan contoh ini:

Misalnya, ekspresi lambda umum ini berisi pernyataan:

auto L = [](const auto& x, auto& y){ return x + y; };

dapat mengakibatkan pembuatan tipe closure, dan objek yang berperilaku mirip dengan struct di bawah ini:

struct /* anonymous */
{
    template <typename T, typename U>
    auto operator()(const T& x, U& y) const // N3386 Return type deduction
    { return x + y; }
} L;
Cassio Neri
sumber