Apakah fungsi lambda dapat di-templated?

230

Di C ++ 11, apakah ada cara untuk templat fungsi lambda? Atau apakah secara inheren terlalu spesifik untuk dicampuri?

Saya mengerti bahwa saya dapat mendefinisikan kelas templated klasik / functor sebagai gantinya, tetapi pertanyaannya lebih seperti: apakah bahasa mengizinkan templating fungsi lambda?

Klaim
sumber
Apakah ada kasus penggunaan di mana templat lambda akan berguna?
James McNellis
7
James: Anda dapat membangun fungsi untuk beralih di atas tuple (Tidak selalu berguna).
Joe D
Saya memikirkan ide itu ketika membaca sebuah wawancara dengan Stroustrup yang berbicara tentang kompleksitas meta-template menjadi masalah. Jika diizinkan, saya membayangkan kode-fu ninja yang mungkin ditemukan oleh programmer yang terlalu pintar bermain dengan kombinasi fitur ini ...
Klaim

Jawaban:

181

UPDATE 2018: C ++ 20 akan datang dengan lambdas templated dan dikonsep. Fitur ini telah diintegrasikan ke dalam konsep standar.


UPDATE 2014: C ++ 14 telah dirilis tahun ini dan sekarang memberikan lambdas Polymorphic dengan sintaks yang sama seperti dalam contoh ini. Beberapa kompiler utama sudah mengimplementasikannya.


Saat berdiri (dalam C ++ 11), sayangnya tidak. Lambdas polymorphic akan sangat baik dalam hal fleksibilitas dan kekuatan.

Alasan asli mereka akhirnya menjadi monomorfik adalah karena konsep. Konsep membuat situasi kode ini sulit:

template <Constraint T>
void foo(T x)
{
    auto bar = [](auto x){}; // imaginary syntax
}

Dalam templat terbatas Anda hanya bisa memanggil templat terbatas lainnya. (Kalau tidak, kendala tidak dapat diperiksa.) Dapat foomeminta bar(x)? Kendala apa yang dimiliki lambda (parameter untuk itu hanyalah templat)?

Konsep tidak siap untuk menangani hal semacam ini; itu akan membutuhkan lebih banyak hal seperti late_check(di mana konsep tidak diperiksa sampai dipanggil) dan hal-hal lainnya. Simpler hanya membuang semua itu dan menempel pada lambda monomorfik.

Namun, dengan menghilangkan konsep dari C ++ 0x, lambdas polimorfik menjadi proposisi sederhana lagi. Namun, saya tidak dapat menemukan proposal untuk itu. :(

GManNickG
sumber
5
Sederhana ... kecuali ada keinginan untuk memperkenalkan kembali konsep dan menghindari fitur yang membuatnya rumit.
6
Saya pikir saya lebih suka memiliki lambdas polimorfik daripada konsep. Saya tidak mengerti bagaimana contoh itu memotivasi apa pun; Anda dapat dengan mudah melarangnya sebagai kesalahan, dan mengharuskan lambda menjadi monomorfik [] (T x) {} atau templat yang dibatasi [] templat <Kendala T> (T x) {}, yang dapat diverifikasi secara statis agar sesuai. Apakah ada alasan mengapa ini tidak mungkin?
DrPizza
13
Anda tidak harus memilih antara konsep dan lambdas polimorfik: cpp-next.com/archive/2011/12/a-pengobosan-untuk-konsep
Dave Abrahams
3
Berikut adalah proposal untuk lambdas polimorfik: open-std.org/jtc1/sc22/wg21/docs/papers/2012/n3418.pdf dan implementasi mainan di dentang: faisalv.github.com/clang-glambda
Radif Sharafullin
18
Lambdas Polymorphic akan berada di C ++ 14, setidaknya mereka sudah ada dalam Draft Komunitas sekarang :)
Arne Mertz
37

C ++ 11 lambdas tidak dapat di-templated seperti yang dinyatakan dalam jawaban lain tetapi decltype()tampaknya membantu ketika menggunakan lambda dalam kelas atau fungsi templated.

#include <iostream>
#include <string>

using namespace std;

template<typename T>
void boring_template_fn(T t){
    auto identity = [](decltype(t) t){ return t;};
    std::cout << identity(t) << std::endl;
}

int main(int argc, char *argv[]) {
    std::string s("My string");
    boring_template_fn(s);
    boring_template_fn(1024);
    boring_template_fn(true);
}

Cetakan:

My string
1024
1

Saya telah menemukan teknik ini sangat membantu ketika bekerja dengan kode templated tetapi menyadari itu masih berarti lambdas sendiri tidak dapat di-templated.

Joel
sumber
26
Takan berfungsi dengan baik di tempat decltype(t)dalam contoh ini.
user2023370
26

Dalam C ++ 11, fungsi lambda tidak dapat di-templated, tetapi dalam versi selanjutnya dari ISO C ++ Standard (sering disebut C ++ 14), fitur ini akan diperkenalkan. [Sumber]

Contoh penggunaan:

auto get_container_size = [] (auto container) { return container.size(); };

Perhatikan bahwa meskipun sintaks menggunakan kata kunci auto, pengurangan tipe tidak akan menggunakan aturan autopengurangan tipe, tetapi sebaliknya menggunakan aturan pengurangan argumen template. Juga lihat proposal untuk ekspresi lambda generik (dan pembaruan untuk ini).

Timo Türschmann
sumber
5
Aturan autopengurangan tipe secara khusus didefinisikan sama dengan aturan deduksi templateargumen fungsi.
underscore_d
10

Saya sadar bahwa pertanyaan ini tentang C ++ 11. Namun, bagi mereka yang googled dan mendarat di halaman ini, lambda templated sekarang didukung dalam C ++ 14 dan menggunakan nama Generic Lambdas.

[info] Sebagian besar kompiler populer mendukung fitur ini sekarang. Microsoft Visual Studio 2015 mendukung. Dentang mendukung. GCC mendukung.

Ram
sumber
6

Saya bertanya-tanya bagaimana dengan ini:

template <class something>
inline std::function<void()> templateLamda() {
  return [](){ std::cout << something.memberfunc() };
}

Saya menggunakan kode serupa seperti ini, untuk menghasilkan template dan bertanya-tanya apakah kompiler akan mengoptimalkan fungsi "pembungkus".

ted
sumber
2
Kompiler apa? Melakukannya?
NicoBerrogorry
3

Ada ekstensi gcc yang memungkinkan templat lambda :

// create the widgets and set the label
base::for_each(_widgets, [] <typename Key_T, typename Widget_T>
                         (boost::fusion::pair<Key_T, Widget_T*>& pair) -> void {
                             pair.second = new Widget_T();
                             pair.second->set_label_str(Key_T::label);
                          }
              );

dimana _widgetsastd::tuple< fusion::pair<Key_T, Widget_T>... >

pengguna6559931
sumber
FWIW, ini telah menjadi sintaks standar dalam C ++ 20.
LF
2

Saya telah bermain dengan version 5.0.1kompilasi dentang terbaru dengan -std=c++17bendera dan sekarang ada beberapa dukungan yang bagus untuk parameter tipe otomatis untuk lambdas:

#include <iostream>
#include <vector>
#include <stdexcept>

int main() {
    auto slice = [](auto input, int beg, int end) {
        using T = decltype(input);
        const auto size = input.size();
        if (beg > size || end > size || beg < 0 || end < 0) {
            throw std::out_of_range("beg/end must be between [0, input.size())");
        }
        if (beg > end) {
            throw std::invalid_argument("beg must be less than end");
        }
        return T(input.begin() + beg, input.begin() + end);
    };
    auto v = std::vector<int> { 1,2,3,4,5 };
    for (auto e : slice(v, 1, 4)) {
        std::cout << e << " ";
    }
    std::cout << std::endl;
}
Doug Coburn
sumber
1

Inilah salah satu solusi yang melibatkan membungkus lamba dalam suatu struktur:

template <typename T>                                                   
struct LamT                                                             
{                                                                       
   static void Go()                                                     
   {                                                                    
      auto lam = []()                                                   
      {                                                                 
         T var;                                                         
         std::cout << "lam, type = " << typeid(var).name() << std::endl;
      };                                                                

      lam();                                                            
   }                                                                    
};   

Untuk menggunakan lakukan:

LamT<int>::Go();  
LamT<char>::Go(); 
#This prints 
lam, type = i
lam, type = c

Masalah utama dengan ini (selain mengetik tambahan) Anda tidak dapat menanamkan definisi struktur ini di dalam metode lain atau Anda dapatkan (gcc 4.9)

error: a template declaration cannot appear at block scope

Saya juga mencoba melakukan ini:

template <typename T> using LamdaT = decltype(                          
   [](void)                                                          
   {                                                                 
       std::cout << "LambT type = " << typeid(T).name() << std::endl;  
   });

Dengan harapan saya bisa menggunakannya seperti ini:

LamdaT<int>();      
LamdaT<char>();

Tapi saya mendapatkan kesalahan kompilator:

error: lambda-expression in unevaluated context

Jadi ini tidak bekerja ... tetapi bahkan jika itu mengkompilasi itu akan menjadi penggunaan terbatas karena kita masih harus meletakkan "menggunakan LamdaT" pada ruang lingkup file (karena itu adalah template) yang semacam mengalahkan tujuan dari lambda.

rmccabe3701
sumber
1

Saya tidak yakin mengapa tidak ada orang lain yang menyarankan ini, tetapi Anda dapat menulis fungsi templated yang mengembalikan fungsi lambda. Berikut ini memecahkan masalah saya, alasan saya datang ke halaman ini:

template <typename DATUM>
std::function<double(DATUM)> makeUnweighted() {
  return [](DATUM datum){return 1.0;};
}

Sekarang setiap kali saya ingin fungsi yang mengambil jenis argumen tertentu (misalnya std::string), saya hanya mengatakan

auto f = makeUnweighted<std::string>()

dan sekarang f("any string")kembali 1.0.

Itulah contoh yang saya maksud dengan "fungsi lambda templated." (Kasus khusus ini digunakan untuk secara otomatis menyediakan fungsi pembobotan inert ketika seseorang tidak ingin menimbang data mereka, apa pun data mereka.)

Jim Pivarski
sumber
2
Ini hanya berfungsi jika Anda tahu jenis argumen dari lambda sebelum membuat lambda, dalam hal ini Anda bisa menggunakan lambda dengan tipe spesifik sebagai argumen. Maksud dari lambda polimorfik adalah untuk menyediakan pekerjaan yang harus dilakukan pada tipe argumen yang Anda tidak pernah tahu ketika Anda menulis kode kerja. Pada dasarnya, ini sangat berbeda, oleh karena itu tidak disarankan.
Klaim
Ah, benar, mengerti. Saya tidak berpikir tentang use case itu --- saya menganggap fungsi lambda sebagai hal-hal on-the-fly dan polimorfisme semacam itu sebagai sesuatu di perpustakaan multiguna. Saya sedang menulis perpustakaan templated yang perlu menerima fungsi lambda pengguna dari jenis apa pun dan juga menyediakan default dari jenis yang tepat.
Jim Pivarski