Подтвердить что ты не робот

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

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

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" не соответствует типам аргументов list: (std:: map, std:: allocator → , __lambda1)

Любая идея, что я делаю неправильно? Компилятор Visual Studio 2008 и Intel С++ 11.1

4b9b3361

Ответ 1

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

В С++ существуют, в общем, 3 типа "вызываемых объектов".

  • Указатели функций.
  • Объекты функций.
  • Лямбда-функции.

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

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

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

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

template<typename T, typename U, typename F> 
map<T,U> mapMapValues(map<T,U> old, F f) 

Ответ 2

Объявление типа параметра T (f)(T,U) имеет свободную функцию типа с T и a 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(...);
}

Ответ 3

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

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

} // ()

Ответ 4

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

Ответ 5

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

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