Напишите функцию, которая принимает лямбда-выражение в качестве аргумента - PullRequest
35 голосов
/ 08 июля 2010

У меня есть такой метод

template<typename T, typename U>
map<T,U> mapMapValues(map<T,U> old, T (f)(T,U))
{
    map<T,U> new;
    for(auto it = old.begin(); it != old.end(); ++it)
    {
        new[it->first] = f(it->first,it->second);
    }
    return new; 
}

и идея в том, что вы бы назвали это так

BOOST_AUTO_TEST_CASE(MapMapValues_basic)
{
    map<int,int> test;
    test[1] = 1;
    map<int,int> transformedMap = VlcFunctional::mapMapValues(test, 
        [&](int key, int value) -> int
        {
            return key + 1; 
        }
    );
}

Однако я получаю ошибку: ни один экземпляр шаблона функции "VlcFunctional :: mapMapValues" не соответствует списку аргументов. Типы аргументов списка: (std :: map, std :: allocator >>, __lambda1)

Есть идеи, что я делаю не так? Компилятор Visual Studio 2008 и Intel C ++ 11.1

Ответы [ 5 ]

37 голосов
/ 08 июля 2010

Ваша функция ожидает указатель на функцию, а не лямбду.

В C ++, как правило, есть 3 типа "вызываемых объектов".

  1. Функциональные указатели.
  2. Функциональные объекты.
  3. Лямбда-функции.

Если вы хотите использовать все это в своем интерфейсе функций, вы можете использовать std::function:

template<typename T, typename U> 
map<T,U> mapMapValues(map<T,U> old, std::function<T(T, U)> f)
{
    ...
}

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

В качестве альтернативы, вы можете добавить дополнительный параметр шаблона, чтобы получить произвольный тип для второго параметра. Это будет более эффективным, но вы потеряете безопасность типов в используемой функции и можете привести к увеличению объема кода.

template<typename T, typename U, typename F> 
map<T,U> mapMapValues(map<T,U> old, F f) 
12 голосов
/ 08 июля 2010

Ваше объявление типа параметра T (f)(T,U) имеет тип 'свободная функция, принимающая T и U и возвращающая T'. Вы не можете передать ему лямбду, объект функции или что-либо, кроме действительной функции с этой сигнатурой.

Вы можете решить эту проблему, изменив тип параметра на std::function<T(T,U)> следующим образом:

template<typename T, typename U> 
map<T,U> mapMapValues(map<T,U> old, std::function<T(T,U)>)
{
}

В качестве альтернативы, вы можете объявить тип функции в качестве аргумента шаблона следующим образом:

template<typename T, typename U, typename Fn> 
map<T,U> mapMapValues(map<T,U> old, Fn fn)
{
  fn(...);
}
10 голосов
/ 24 декабря 2014

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

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

} // ()
5 голосов
/ 09 июля 2010

Лямбда-выражения с пустым списком перехвата должны исчезнуть из-за указателей функций, согласно n3052 .Однако кажется, что эта функция не реализована в VC ++ и только частично в g ++, см. Мой вопрос SO .

1 голос
/ 12 октября 2016

Вот пример того, как передать функцию в качестве параметра

class YourClass
{
void YourClass::callback(void(*fptr)(int p1, int p2))
{
    if(fptr != NULL)
      fptr(p1, p2);
}
};

void dummyfunction(int p1, int p2)
{
   cout << "inside dummyfunction " << endl;
}

YourClass yc;

// using a dummyfunction as callback
yc.callback(&dummyfunction);

// using a lambda as callback
yc.callback( [&](int p1, int p2) { cout << "inside lambda callback function" << endl; } );

// using a static member function 
yc.callback( &aClass::memberfunction );
Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...