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?
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?
Yanıtlar:
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. :(
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.
T
yerine para cezası çalışacak decltype(t)
bu örnekte.
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ı auto
kullanmayacak, 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 .
auto
Tür çıkarım kuralları, template
işlev bağımsız değişken çıkarımıyla aynı olarak tanımlanır .
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.
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.
Polimorfik lambdalar için Boost.Phoenix'e bir göz atın: http://www.boost.org/doc/libs/1_44_0/libs/spirit/phoenix/doc/html/index.html C ++ 0x gerektirmez. yol :)
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 _widgets
birstd::tuple< fusion::pair<Key_T, Widget_T>... >
Ben bayrak version 5.0.1
derleme son clang ile oynuyordum -std=c++17
ve ş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;
}
İş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.
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.)