Чи можна шаблонувати функції лямбда?


230

Чи є в C ++ 11 спосіб шаблонування лямбда-функції? Або вона за своєю суттю занадто специфічна, щоб бути шаблоною?

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


Чи є випадок використання, коли лямбда-шаблон буде корисним?
Джеймс Мак-Нілліс

7
Джеймс: Ви можете побудувати функцію для повторення кортежу (не обов'язково корисного).
Джо D

Я подумав про цю ідею, читаючи інтерв'ю Stroustrup, і говорив про те, що складність мета-шаблону є проблемою. Якби це було дозволено, я уявляв собі ніндзя код-фу, який, можливо, винайдуть занадто розумні програмісти, які грають з цією комбінацією можливостей ...
Клайм

Відповіді:


181

ОНОВЛЕННЯ 2018: С ++ 20 прийдуть із шаблонованими та концептуалізованими лямбдами. Функція вже інтегрована в стандартний проект.


ОНОВЛЕННЯ 2014: C ++ 14 було випущено цього року і тепер надає поліморфним лямбдам такий же синтаксис, як у цьому прикладі. Деякі основні компілятори вже реалізують це.


На ньому стоїть (у С ++ 11), на жаль ні. Поліморфні лямбда були б чудовими з точки зору гнучкості та потужності.

Первісна причина, по якій вони виявилися мономорфними, була через поняття. Концепції ускладнили цю ситуацію з кодом:

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

У обмеженому шаблоні ви можете викликати лише інші обмежені шаблони. (Інакше обмеження неможливо перевірити.) Можна fooвикликати bar(x)? Які обмеження має лямбда (зрештою, параметр для неї є лише шаблоном)?

Концепції не були готові вирішувати подібні речі; знадобиться більше речей, як-от late_check(де концепція не була перевірена до виклику) та ін. Простіше було просто кинути все це і дотримуватися мономорфних лямбдів.

Однак із вилученням понять із C ++ 0x, поліморфні лямбдалі знову стають простим пропозицією. Однак я не можу знайти жодних пропозицій щодо цього. :(


5
Просте ... за винятком бажання знову ввести поняття та уникати особливостей, які ускладнюють їх.

6
Я думаю, що я вважаю за краще поліморфні лямбда, ніж поняття. Я не розумію, як приклад щось мотивує; ви можете просто заборонити це як помилку і вимагати, щоб лямбда була мономорфною [] (T x) {} або обмеженим шаблоном [] шаблон <] <Обмеження T> (T x) {}, який може статично перевіряти відповідність. Чи є якась причина, чому це було неможливо?
DrPizza

13
Вам не доведеться вибирати між поняттями та поліморфними лямбдами: cpp-next.com/archive/2011/12/a-breakthrough-for-concepts
Дейв Абрахамс

3
Ось пропозиція щодо поліморфних лямбдів: open-std.org/jtc1/sc22/wg21/docs/papers/2012/n3418.pdf та реалізація іграшки в клані
Radif Sharafullin

18
Поліморфні лямбдати будуть у С ++ 14, принаймні вони вже є у Чернеті Спільноти :)
Арне Мерц

37

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

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


26
Tбуде добре працювати замість decltype(t)цього прикладу.
користувач2023370

26

У C ++ 11 функції лямбда неможливо шаблонувати, але в наступній версії стандарту ISO C ++ (часто її називають C ++ 14) ця функція буде введена. [Джерело]

Приклад використання:

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

Зауважте, що хоча синтаксис використовує ключове слово auto, дедукція типу не буде використовувати правила autoвиведення типу, а натомість використовувати правила виведення аргументу шаблону. Також дивіться пропозицію щодо загальних лямбда-виразів (та оновлення до цього).


5
Правила autoдедукції типу конкретно визначені так, щоб вони були тими ж, що і у templateфункцій виведення аргументів функції.
підкреслюйте_

10

Мені відомо, що це питання стосується C ++ 11. Однак для тих, хто перебуває в Google і приземлився на цій сторінці, шаблонні лямбдати тепер підтримуються в 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, автор спосіб :)


2
Я вже знаю про це, але питання саме про новий стандарт все одно;)
Klaim

Добре :) C ++ 0x лямбда є мономорфними і, на жаль, не можуть бути шаблонні.
usta

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>... >


FWIW, це стало стандартним синтаксисом в C ++ 20.
LF

2

Я грав з останнім клангом, що 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

Ось одне рішення, яке передбачає вкручування ягняти в структуру:

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" у область файлу (тому що це шаблон), яка ніби перемагає мету лямбда.


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
Це працює лише в тому випадку, якщо ви знаєте тип аргументу для лямбда перед створенням лямбда, і в цьому випадку ви можете просто використовувати лямбда з конкретним типом як аргумент. Поліморфна лямбда полягає в тому, щоб забезпечити роботу над типом аргументу, про який ви ніколи не знаєте, коли пишете робочий код. В основному, це зовсім інше, через що не пропонувалося.
Клаїм

Ага, правда, зрозумів. Я не думав про такий випадок використання --- я думаю про функції лямбда як про речі, які використовуються, і такий поліморфізм, як щось у багатоцільовій бібліотеці. Я писав шаблонову бібліотеку, яка повинна приймати лямбда-функції будь-якого типу користувача, а також надавати типові параметри потрібного типу.
Джим Піварський
Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.