Lambda fonksiyonları ayarlanabiliyor mu?


230

C ++ 11'de lambda işlevini şablonlamanın bir yolu var mı? Yoksa tabiatı gereği çok özel mi?

Bunun yerine klasik şablonlu sınıf / functor tanımlayabildiğimi anlıyorum, ancak soru daha çok benziyor: dil lambda işlevlerini ayarlamaya izin veriyor mu?


Lambda şablonunun yararlı olacağı bir kullanım durumu var mı?
James McNellis

7
James: Bir demet üzerinde yineleme yapmak için bir fonksiyon oluşturabilirsin (Faydalı değil).
Joe D

Stroustrup'un meta-şablon karmaşıklığının bir sorun olduğunu söyleyerek yaptığı röportajı okurken fikri düşündüm. İzin verildiyse, bu özellik kombinasyonu ile oynayan çok zeki programcılar tarafından icat edilebilecek ninja kodu-fu hayal ediyordum ...
Klaim

Yanıtlar:


181

GÜNCELLEME 2018: C ++ 20 templated ve kavramsallaştırılmış lambdalarla gelecek. Özellik zaten standart taslağa entegre edildi.


GÜNCELLEME 2014: C ++ 14 bu yıl piyasaya sürüldü ve şimdi Polimorfik lambdalara bu örnekteki ile aynı sözdizimini sağlıyor. Bazı büyük derleyiciler zaten uygulamaktadır.


Üzerinde duruyor (C ++ 11'de), ne yazık ki hayır. Polimorfik lambdalar esneklik ve güç açısından mükemmel olacaktır.

Monomorfik olmalarının asıl nedeni kavramlardı. Kavramlar bu kod durumunu zorlaştırdı:

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

Kısıtlanmış bir şablonda yalnızca diğer kısıtlanmış şablonları çağırabilirsiniz. (Aksi takdirde kısıtlamalar kontrol edilemedi.) fooÇağırabilir bar(x)mi? Lambda'nın ne gibi kısıtlamaları var (bunun için parametre sadece bir şablon, sonuçta)?

Kavramlar bu tür şeylerle baş etmeye hazır değildi; late_check(kavramın çağrılıncaya kadar kontrol edilmediği yerlerde) ve daha fazla şey gerektirir . Daha basit, her şeyi bırakmak ve monomorfik lambdaslara sadık kalmaktı.

Bununla birlikte, kavramların C ++ 0x'den kaldırılmasıyla, polimorfik lambdalar tekrar basit bir öneri haline gelir. Ancak bunun için herhangi bir teklif bulamıyorum. :(


5
Basit ... kavramları yeniden dahil etme ve karmaşık hale getiren özelliklerden kaçınma arzusu dışında.

6
Bence kavramlardan ziyade polimorfik lambdaları tercih ederim. Örneğin bir şeyi nasıl motive ettiğini anlamıyorum; bunu bir hata olarak yasaklayabilir ve lambda'nın monomorfik [] (T x) {} veya statik olarak eşleşecek şekilde doğrulanabilen kısıtlanmış bir şablon [] şablonu <Kısıtlama T> (T x) {} olmasını isteyebilirsiniz. Bunun mümkün olmamasının bir nedeni var mı?
DrPizza

13
Kavramlar ve polimorfik lambdalar arasında seçim yapmak zorunda değilsiniz: cpp-next.com/archive/2011/12/a-breakthrough-for-concepts
Dave Abrahams


18
Polimorfik Lambdalar C ++ 14'te olacak, en azından şimdiye kadar Topluluk Taslağı'nda olacaklar :)
Arne Mertz

37

C ++ 11 lambdas, diğer cevaplarda belirtildiği gibi şablonlandırılamaz, ancak decltype()şablonlaştırılmış bir sınıf veya işlev içinde lambda kullanırken yardımcı olur.

#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);
}

Baskılar:

My string
1024
1

Ben bu teknik templated kod ile çalışırken yardımcı olduğunu bulduk ama hala lambdas kendilerini templated olamaz anlamına gelir.


26
Tyerine para cezası çalışacak decltype(t)bu örnekte.
user2023370

26

C ++ 11'de lambda işlevleri ayarlanamaz, ancak ISO C ++ Standardının (genellikle C ++ 14 olarak adlandırılır) sonraki sürümünde bu özellik tanıtılacaktır. [Kaynak]

Kullanım örneği:

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

Sözdizimi anahtar kelimeyi kullanıyor olsa da auto, tür kesinti tür kesinti kurallarını autokullanmayacak, bunun yerine şablon bağımsız değişken kesinti kurallarını kullanacaktır. Ayrıca genel lambda ifadeleri (ve bunun güncellenmesi ) önerisine bakın .


5
autoTür çıkarım kuralları, templateişlev bağımsız değişken çıkarımıyla aynı olarak tanımlanır .
underscore_d

10

Bu sorunun C ++ 11 ile ilgili olduğunun farkındayım. Ancak, bu sayfaya googled ve indi olanlar için, şablonlu lambdas artık C ++ 14'te destekleniyor ve Generic Lambdas adıyla destekleniyor.

[info] Popüler derleyicilerin çoğu şimdi bu özelliği desteklemektedir. Microsoft Visual Studio 2015 destekler. Clang destekler. GCC destekler.


6

Bunun ne olduğunu merak ediyorum:

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

Bir şablon oluşturmak ve derleyicinin "sarma" işlevini optimize edip etmeyeceğini merak etmek için böyle bir kod kullandım.


2
Ne derleyici? Öyle mi?
NicoBerrogorry


3

Lambda şablonlarına izin veren bir gcc uzantısı var :

// 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);
                          }
              );

nerede _widgetsbirstd::tuple< fusion::pair<Key_T, Widget_T>... >


FWIW, bu C ++ 20'de standart sözdizimi haline geldi.
LF

2

Ben bayrak version 5.0.1derleme son clang ile oynuyordum -std=c++17ve şimdi lambdas için otomatik tip parametreleri için bazı güzel destek var:

#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;
}

1

İşte lambanın bir yapıya sarılmasını içeren bir çözüm:

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

      lam();                                                            
   }                                                                    
};   

Kullanmak için:

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

Bununla ilgili ana sorun (ekstra yazmanın yanı sıra), bu yapı tanımını başka bir yöntemin içine yerleştiremezsiniz veya elde edersiniz (gcc 4.9)

error: a template declaration cannot appear at block scope

Ayrıca bunu denedim:

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

Bu şekilde kullanabilme umuduyla:

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

Ama derleyici hatası alıyorum:

error: lambda-expression in unevaluated context

Yani bu işe yaramaz ... ama derlemiş olsa bile sınırlı bir kullanım olurdu çünkü hala "LamdaT kullanarak" ı dosya kapsamına (bir şablon olduğu için) hangi tür mağlubiyetin amacını lambda'lar.


1

Neden başka kimsenin bunu önermediğinden emin değilim, ancak lambda işlevlerini döndüren şablonlu bir işlev yazabilirsiniz. Aşağıdakiler sorunumu çözdü, bu sayfaya gelmem sebebi:

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

Şimdi belirli bir tür argüman alan bir işlev istediğimde (örneğin std::string)

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

ve şimdi f("any string")geri dönüyor 1.0.

Bu, "şablonlu lambda fonksiyonu" ile kastettiğim şeyin bir örneği. (Bu özel durum, verileri ne olursa olsun birileri verilerini ağırlıklandırmak istemediğinde otomatik olarak etkisiz bir ağırlıklandırma işlevi sağlamak için kullanılır.)


2
Bu sadece lambda'yı oluşturmadan önce lambda argümanının türünü biliyorsanız çalışır, bu durumda sadece belirli türdeki bir lambda'yı argüman olarak kullanabilirsiniz. Polimorfik lambda'nın amacı, çalışma kodunu yazarken asla bilmediğiniz bir argüman türünde yapılacak işi sağlamaktır. Temel olarak, bu tamamen farklı, bu yüzden önerilmedi.
Klaim

Ah, doğru, anladım. Ben bu kullanım örneğini düşünmedim --- lambda fonksiyonlarını anında ve bu tür bir polimorfizmi çok amaçlı bir kütüphanede bir şey olarak düşünüyorum. Kullanıcının herhangi bir türdeki lambda işlevlerini kabul etmesi ve ayrıca doğru türün varsayılanlarını sağlaması gereken şablonlu bir kütüphane yazıyordum.
Jim Pivarski
Sitemizi kullandığınızda şunları okuyup anladığınızı kabul etmiş olursunuz: Çerez Politikası ve Gizlilik Politikası.
Licensed under cc by-sa 3.0 with attribution required.