Як оголосити функцію, яка приймає лямбда-звук?


83

Я прочитав в Інтернеті багато підручників, які пояснювали, як користуватися лямбдами зі стандартною бібліотекою (наприклад, std::find), і всі вони були дуже цікавими, але я не міг знайти жодного, який би пояснив, як я можу використовувати лямбду для своїх власних функцій.

Наприклад:

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

    return EXIT_SUCCESS;
}

Як я повинен заявити LambdaTest? Який тип першого аргументу? А потім, як я можу назвати анонімну функцію, що передається їй - наприклад, "10" як аргумент?

Відповіді:


80

Враховуючи, що ви, мабуть, також хочете приймати покажчики на функції та об'єкти функцій на додаток до лямбда, ви, мабуть, захочете використовувати шаблони, щоб прийняти будь-який аргумент з operator(). Це те, що роблять std-функції типу find. Це виглядало б так:

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

Зверніть увагу, що це визначення не використовує жодних функцій c ++ 0x, тому воно повністю сумісне із зворотною стороною. Це лише виклик функції з використанням лямбда-виразів, що є специфічним для c ++ 0x.


3
У разі помилок повідомлення про помилки буде важко зрозуміти.
liori

15
Це залежить від того, чи найкращий. Тут використовується шаблон, а в іншому - ні. Це означає, що функція більше не може бути віртуальною і не може бути визначена окремо у файлі cpp. std::functionчудово вміє також приймати типи функціональних класів, хоча і трохи повільніше під час виклику. Але ця різниця незначна для більшості додатків :)
Йоханнес Шауб - літ

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 ();

} // ()

2
Вам не потрібні funValid: en.cppreference.com/w/cpp/utility/functional/function/… , просто скажітьif ( ! theFunction )
Erik Aronesty
Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.