Как объявить функцию, принимающую лямбду?

83

Я прочитал в Интернете множество руководств, в которых объяснялось, как использовать лямбды со стандартной библиотекой (например, std::find ), и все они были очень интересными, но я не мог найти ни одного, объясняющего, как я могу использовать лямбда-выражения для моих собственных функций.

Например:

int main()
{
    int test = 5;
    LambdaTest([&](int a) { test += a; });

    return EXIT_SUCCESS;
}

Как мне заявить LambdaTest? Каков тип его первого аргумента? И затем, как я могу вызвать анонимную функцию, передаваемую ей - например, - «10» в качестве аргумента?

Томас Бонини
источник

Ответы:

80

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

template<typename Func>
void LambdaTest(Func f) {
    f(10);
}

Обратите внимание, что это определение не использует никаких функций c ++ 0x, поэтому оно полностью обратно совместимо. Это только вызов функции с использованием лямбда-выражений, специфичных для C ++ 0x.

sepp2k
источник
3
В случае ошибок сообщения об ошибках будет трудно понять.
Лиори
15
Это зависит от того, лучший ли он. Здесь используется шаблон, а в другом - нет. Это означает, что функция больше не может быть виртуальной и не может быть определена отдельно в файле cpp. std::functionтакже отлично может принимать типы классов объектов функций, хотя при вызове работает немного медленнее. Но эта разница незначительна для большинства приложений :)
Йоханнес Шауб - litb
3
«лучший» - в глазах смотрящего :-) в этом ответе используется functorхороший, но не совсем ответ на исходный вопрос (и что меня сюда привело), ​​а именно «как мне использовать лямбду для моих собственных функций» . Кроме того, у шаблонов есть свой собственный набор проблем, на который нет ответа std::function.
Марко Массенцио
2
@Marco Этот ответ не требует использования функций, он позволяет использовать все, что вы хотите, включая лямбды.
sepp2k
73

Если вы не хотите использовать шаблон для всего, вы можете сделать следующее:

#include<functional> 

void LambdaTest (const std::function <void (int)>& f)
{
    ...
}
удваивать
источник
1
Этот синтаксис на самом деле позволяет мне сохранить переменную функции, чтобы вызвать ее позже, верно? Например, я хотел реализовать функцию, позволяющую выполнять асинхронные запросы к базе данных, где лямбда действует как обратный вызов. (Конечно, я не смог бы получить доступ к закрытию по ссылке)
Томас Бонини
1
Не более ли идиоматично передавать функции по значению?
fredoverflow
3
@Andreas Bonini: Да, если вы сохраните std::function(не в качестве ссылки), вы сделаете копию f. Однако я не уверен, как лямбда / замыкания обрабатывают ссылки, когда упомянутый объект выходит за пределы области видимости, возможно, UB. @FredOverflow: Насколько я понимаю, std::functionэто нетривиальный объект, особенно при переносе лямбда-выражений. Вероятно, лучше ссылаться на него, чтобы избежать ненужного копирования.
даблп
Если ваша лямбда захватывает стек по значению, тогда да, лямбда может пережить эти переменные и будет продолжать сохранять их копии. Если захватить по ссылке, у вас возникнут проблемы.
Кейт Грегори
1
Нам нужно будет скопировать его внутри функции, поэтому нет смысла копировать его, когда он передается
Casebash
9

Я хотел бы предложить этот простой, но не требующий пояснений пример. Он показывает, как передавать «вызываемые объекты» (функции, функциональные объекты и лямбда-выражения) в функцию или объект.

// g++ -std=c++11 thisFile.cpp

#include <iostream>
#include <thread>

using namespace std;

// -----------------------------------------------------------------
class Box {
public:
  function<void(string)> theFunction; 
  bool funValid;

  Box () : funValid (false) { }

  void setFun (function<void(string)> f) {
    theFunction = f;
    funValid = true;
  }

  void callIt () {
    if ( ! funValid ) return;
    theFunction (" hello from Box ");
  }
}; // class

// -----------------------------------------------------------------
class FunClass {
public:
  string msg;
  FunClass (string m) :  msg (m) { }
  void operator() (string s) {
    cout << msg <<  s << endl; 
  }
};

// -----------------------------------------------------------------
void f (string s) {
  cout << s << endl;
} // ()

// -----------------------------------------------------------------
void call_it ( void (*pf) (string) ) {
  pf( "call_it: hello");
} // ()

// -----------------------------------------------------------------
void call_it1 ( function<void(string)> pf ) {
  pf( "call_it1: hello");
} // ()

// -----------------------------------------------------------------
int main() {

  int a = 1234;

  FunClass fc ( " christmas ");

  f("hello");

  call_it ( f );

  call_it1 ( f );

  // conversion ERROR: call_it ( [&] (string s) -> void { cout << s << a << endl; } );

  call_it1 ( [&] (string s) -> void { cout << s << a << endl; } );

  Box ca;

  ca.callIt ();

  ca.setFun (f);

  ca.callIt ();

  ca.setFun ( [&] (string s) -> void { cout << s << a << endl; } );

  ca.callIt ();

  ca.setFun (fc);

  ca.callIt ();

} // ()
cibercitizen1
источник
2
Вам не нужен funValid: en.cppreference.com/w/cpp/utility/functional/function/… , просто скажитеif ( ! theFunction )
Эрик Аронести