Напишите функцию, которая принимает лямбда-выражение в качестве аргумента
У меня есть такой метод
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
Ответы
Ответ 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 );