Могут ли лямбда-функции быть шаблонными? - PullRequest
205 голосов
/ 26 августа 2010

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

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

Ответы [ 10 ]

160 голосов
/ 26 августа 2010

ОБНОВЛЕНИЕ 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 полиморфные лямбды снова становятся простым предложением. Однако я не могу найти никаких предложений для этого. (

34 голосов
/ 08 августа 2013

C ++ 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

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

23 голосов
/ 18 ноября 2013

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

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

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

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

9 голосов
/ 30 декабря 2014

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

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

6 голосов
/ 19 декабря 2011

Интересно, что по этому поводу:

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

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

4 голосов
/ 05 сентября 2010

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

3 голосов
/ 07 июля 2016

Существует расширение 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>... >

1 голос
/ 21 февраля 2018

Я играл с последним 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;
}
1 голос
/ 27 апреля 2016

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

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.

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

0 голосов
/ 05 августа 2015

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

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

      lam();                                                            
   }                                                                    
};   

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

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

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

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...