Могут ли использоваться шаблоны лямбда?

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

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

Ответ 1

UPDATE 2014: С++ 14 были выпущены в этом году и теперь предоставляют Polymorphic lambdas с тем же синтаксисом, что и в этом примере. Некоторые основные компиляторы уже реализуют его.


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

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

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

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

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

Однако, с удалением понятий из С++ 0x, полиморфные лямбды снова становятся простым предложением. Однако я не могу найти для этого никаких предложений.: (

Ответ 2

С++ 11 lambdas нельзя шаблонировать, как указано в других ответах, но 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

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

Ответ 3

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

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

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

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

Ответ 4

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

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

Ответ 5

Интересно, что об этом:

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

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

Ответ 7

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

Ответ 8

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

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.

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

Ответ 9

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

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

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