Можно ли использовать шаблоны лямбда-функций?

В C ++ 11 есть способ создать шаблон лямбда-функции? Или он слишком специфичен для шаблонов?

Я понимаю, что вместо этого я могу определить классический шаблонный класс / функтор, но вопрос больше похож: позволяет ли язык создавать шаблоны лямбда-функций?


person Klaim    schedule 26.08.2010    source источник
comment
Есть ли вариант использования, когда будет полезен лямбда-шаблон?   -  person James McNellis    schedule 26.08.2010
comment
Джеймс: Вы можете создать функцию для перебора кортежа (не обязательно полезно).   -  person Joe D    schedule 26.08.2010
comment
Я подумал об этой идее, когда читал интервью Страуструпа, в котором говорилось о том, что сложность мета-шаблона является проблемой. Если бы это было разрешено, я представлял себе код-фу ниндзя, который мог бы быть изобретен слишком умными программистами, играющими с этой комбинацией функций ...   -  person Klaim    schedule 26.08.2010
comment
Забавно видеть исторические мнения десятилетней давности.   -  person Johan Boulé    schedule 11.06.2021
comment
Действительно. Теперь я понимаю, чем это может быть полезно. Я также вижу, как им можно злоупотреблять, но можно использовать все полезные инструменты. : гримасничать:   -  person Klaim    schedule 16.06.2021


Ответы (11)


ОБНОВЛЕНИЕ 2018: C ++ 20 будет содержать шаблонные и концептуальные лямбды. Эта функция уже интегрирована в стандартный проект.


ОБНОВЛЕНИЕ 2014: C ++ 14 был выпущен в этом году и теперь предоставляет полиморфные лямбды с тем же синтаксисом, что и в этом примере. Некоторые крупные компиляторы уже реализуют это.


На нем стоит (в C ++ 11), к сожалению, нет. Полиморфные лямбды были бы превосходны с точки зрения гибкости и мощности.

Первоначальная причина того, что они оказались мономорфными, заключалась в концепциях. Концепции усложнили эту ситуацию с кодом:

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

В ограниченном шаблоне вы можете вызывать только другие ограниченные шаблоны. (В противном случае ограничения нельзя было бы проверить.) Может ли foo вызвать bar(x)? Какие ограничения есть у лямбда (в конце концов, параметр для него - всего лишь шаблон)?

Концепции не были готовы заниматься такими вещами; для этого потребовалось бы еще кое-что вроде late_check (где концепция не проверялась до вызова) и прочее. Проще было просто отказаться от всего этого и придерживаться мономорфных лямбд.

Однако с удалением концепций из C ++ 0x полиморфные лямбды снова стали простым предложением. Однако предложений по нему я не нашел. :(

person GManNickG    schedule 26.08.2010
comment
Просто ... за исключением того, что есть желание заново ввести концепции и избежать особенностей, которые их усложняют. - person ; 08.11.2010
comment
Я думаю, что лучше иметь полиморфные лямбды, чем концепции. Я не понимаю, как этот пример что-то мотивирует; вы можете просто запретить это как ошибку и потребовать, чтобы лямбда была мономорфной [] (T x) {} или ограниченным шаблоном [] template ‹Ограничение T› (T x) {}, соответствие которым можно статически проверить. Есть ли причина, по которой это было невозможно? - person DrPizza; 14.07.2011
comment
Вам не нужно выбирать между концепциями и полиморфными лямбдами: cpp-next.com/archive/2011/12/a-breakthrough-for-concepts - person Dave Abrahams; 21.12.2011
comment
Вот предложение для полиморфных лямбд: open- std.org/jtc1/sc22/wg21/docs/papers/2012/n3418.pdf и реализация игрушки в clang: faisalv.github.com/clang-glambda - person Radif Sharafullin; 11.01.2013
comment
Полиморфные лямбды будут в C ++ 14, по крайней мере, они сейчас находятся в проекте сообщества :) - person Arne Mertz; 08.05.2013
comment
@GManNickG Clang теперь поддерживает их в --std = c ++ 17. См. Мой ответ здесь: stackoverflow.com/ questions / 3575901 / - person Doug Coburn; 19.03.2018

Лямбда-выражения С ++ 11 не могут быть шаблонными, как указано в других ответах, но decltype(), похоже, помогает при использовании лямбда-выражения в шаблонном классе или функции.

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

Печать:

My string
1024
1

Я обнаружил, что этот метод помогает при работе с шаблонным кодом, но я понимаю, что это по-прежнему означает, что сами лямбды не могут быть шаблонными.

person Joel    schedule 08.08.2013
comment
T будет отлично работать вместо decltype(t) в этом примере. - person user2023370; 08.11.2015

В C ++ 11 лямбда-функции нельзя создавать по шаблонам, но в следующей версии стандарта ISO C ++ (часто называемого C ++ 14) эта функция будет представлена. [Источник]

Пример использования:

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

Обратите внимание, что хотя в синтаксисе используется ключевое слово auto, при выводе типа не используются правила вывода типа auto, а вместо этого используются правила вывода аргумента шаблона. Также см. предложение для общих лямбда-выраженийобновить до этого).

person Timo Türschmann    schedule 18.11.2013
comment
Правила вывода типа auto специально определены как те же, что и правила вывода аргумента функции template. - person underscore_d; 17.04.2016

В C ++ 20 это возможно с использованием следующего синтаксиса:

auto lambda = []<typename T>(T t){
    // do something
};
person shilch    schedule 16.07.2020
comment
Означает ли это, что теперь мы сможем написать такую ​​лямбду []<>(){}? - person Kostas; 30.10.2020
comment
Если вам нужно явно указать аргументы шаблона при вызове лямбды, что, как я считаю, является общей потребностью в моих случаях использования (например, если параметр T t не существует в приведенном выше примере), вы можете сделать это, используя следующий синтаксис: lambda.template operator()<int>(). См. stackoverflow.com/questions/49392738 для получения дополнительной информации. - person Arda; 10.03.2021

Мне известно, что этот вопрос касается C ++ 11. Однако для тех, кто погуглил и попал на эту страницу, шаблонные лямбды теперь поддерживаются в C ++ 14 и носят название Generic Lambdas.

[info] Большинство популярных компиляторов теперь поддерживают эту функцию. Microsoft Visual Studio 2015 поддерживает. Clang поддерживает. GCC поддерживает.

person Ram    schedule 30.12.2014

Интересно, а как насчет этого:

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

Я использовал похожий код, как этот, чтобы сгенерировать шаблон и задаться вопросом, оптимизирует ли компилятор функцию "обертывания".

person ted    schedule 19.12.2011
comment
Какой компилятор? Сделал это? - person NicoBerrogorry; 14.08.2018

Существует расширение gcc, которое позволяет использовать лямбда-шаблоны:

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

где _widgets - это std::tuple< fusion::pair<Key_T, Widget_T>... >

person user6559931    schedule 07.07.2016
comment
FWIW, это стало стандартным синтаксисом в C ++ 20. - person L. F.; 10.06.2019

Посмотрите на Boost.Phoenix полиморфные лямбды: http://www.boost.org/doc/libs/1_44_0/libs/spirit/phoenix/doc/html/index.html Кстати, не требует C ++ 0x :)

person usta    schedule 05.09.2010
comment
Я уже знаю об этом, но все равно речь идет именно о новом стандарте;) - person Klaim; 05.09.2010
comment
Хорошо :) Лямбды C ++ 0x мономорфны и, к сожалению, не могут быть шаблонизированы. - person usta; 05.09.2010

Я играл с последней компиляцией clang version 5.0.1 с флагом -std=c++17, и теперь есть хорошая поддержка параметров автоматического типа для лямбда-выражений:

#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;
}
person Doug Coburn    schedule 21.02.2018

Вот одно из решений, которое включает в себя упаковку ламбы в структуру:

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

      lam();                                                            
   }                                                                    
};   

Для использования do:

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

Основная проблема с этим (помимо лишнего набора текста): вы не можете встроить это определение структуры в другой метод, иначе вы получите (gcc 4.9)

error: a template declaration cannot appear at block scope

Я тоже пробовал это делать:

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

В надежде, что я смогу использовать это так:

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

Но я получаю ошибку компилятора:

error: lambda-expression in unevaluated context

Так что это не работает ... но даже если бы он был скомпилирован, он имел бы ограниченное использование, потому что нам все равно пришлось бы помещать «using LamdaT» в область действия файла (потому что это шаблон), что своего рода поражает цель лямбды.

person rmccabe3701    schedule 05.08.2015

Я не уверен, почему никто другой не предлагал этого, но вы можете написать шаблонную функцию, которая возвращает лямбда-функции. Следующее решило мою проблему, причина, по которой я пришел на эту страницу:

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

Теперь, когда мне нужна функция, которая принимает аргумент заданного типа (например, std::string), я просто говорю

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

и теперь f("any string") возвращает 1.0.

Это пример того, что я имею в виду под «шаблонной лямбда-функцией». (Этот конкретный случай используется для автоматического предоставления инертной функции взвешивания, когда кто-то не хочет взвешивать свои данные, какими бы они ни были.)

person Jim Pivarski    schedule 27.04.2016
comment
Это работает только в том случае, если вы знаете тип аргумента лямбды до создания лямбда, и в этом случае вы можете просто использовать лямбда с определенным типом в качестве аргумента. Смысл полиморфной лямбды в том, чтобы обеспечить выполнение работы с типом аргумента, о котором вы никогда не узнаете, когда пишете рабочий код. По сути, это совсем другое, поэтому и не предлагалось. - person Klaim; 28.04.2016
comment
Ах, да, понял. Я не думал об этом варианте использования - я думаю о лямбда-функциях как о вещах «на лету», а о таком полиморфизме - как о чем-то в многоцелевой библиотеке. Я писал шаблонную библиотеку, которая должна принимать пользовательские лямбда-функции любого типа, а также предоставлять значения по умолчанию для правильного типа. - person Jim Pivarski; 28.04.2016