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


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.