Могут ли лямбда-функции быть шаблонными?

230

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

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

Klaim
источник
Есть ли случай, когда лямбда-шаблон будет полезен?
Джеймс МакНеллис
7
Джеймс: Вы могли бы создать функцию для итерации по кортежу (не обязательно полезно).
Джо Д
Я подумал об этой идее, когда читал интервью со Страуструпом, в котором говорилось о сложности мета-шаблона. Если бы это было позволено, я представлял себе код-фу ниндзя, которое могли бы придумать слишком умные программисты, играющие с этой комбинацией функций ...
Klaim

Ответы:

181

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

GManNickG
источник
5
Просто ... за исключением того, что есть желание заново ввести понятия и избегать функций, которые усложняют их.
6
Я думаю, что я предпочел бы иметь полиморфные лямбды, чем понятия. Я не понимаю, как пример мотивирует что-либо; Вы можете просто запретить это как ошибку и потребовать, чтобы лямбда была мономорфной [] (T x) {} или ограниченным шаблоном шаблона [] template <Constraint T> (T x) {}, который может быть статически проверен на соответствие. Есть ли какая-то причина, почему это было невозможно?
DrPizza
13
Вам не нужно выбирать между концепциями и полиморфными лямбдами
Дейв Абрахамс
3
Вот предложение о полиморфной лямбде: open-std.org/jtc1/sc22/wg21/docs/papers/2012/n3418.pdf и реализация игрушек в звоне: faisalv.github.com/clang-glambda
Радиф Sharafullin
18
Полиморфные лямбды будут в C ++ 14, по крайней мере, они уже в проекте сообщества :)
Arne Mertz
37

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

#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

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

Joel
источник
26
Tбудет хорошо работать вместо этого decltype(t)в этом примере.
user2023370
26

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

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

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

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

Тимо Тюршман
источник
5
Правила autoвывода типа специально определены так же, как правила вывода templateаргумента функции.
underscore_d
10

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

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

ОЗУ
источник
6

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

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

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

Тед
источник
2
Какой компилятор? Сделал это?
NicoBerrogorry
4

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

USTA
источник
2
Я уже знаю об этом, но все равно вопрос о новом стандарте;)
Klaim
Хорошо :) C ++ 0x лямбды мономорфны и, к сожалению, не могут быть шаблонными.
Уста
3

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

user6559931
источник
FWIW, это стало стандартным синтаксисом в C ++ 20.
LF
2

Я играл с последней version 5.0.1компиляцией clang с -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

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

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

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

rmccabe3701
источник
1

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

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.

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

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