Передать лямбда-выражение в лямбда-аргумент c++11
Я хотел бы сделать что-то вроде этого:
int main()
{
auto f = [/*some variables*/](/*take lambda function*/)
{/*something with lambda function*/};
f([/*other variables*/](/*variables to be decided by f()*/)
{/*something with variables*/});
}
Я знаю, что можно передать лямбда-функции, а также лямбда. Следующие работы:
int main()
{
int x=0;
int y=0;
auto f = [x,y](double (func)(int)) -> double
{func(0); return 0.0;};
f([](int i) -> double
{return 0.0;});
}
но следующее не работает (как только я изменяю переменные области, чтобы добавить [x])
int main()
{
int x=0;
int y=0;
auto f = [x,y](double (func)(int)) -> double
{func(0); return 0.0;}
f([x](int i) -> double //[x] does not work
{return 0.0;});
}
что дает ошибку:
error: function "lambda [](double (*)(int))->double::operator()" cannot be called with the given argument list
argument types are: (lambda [](int)->double)
object type is: lambda [](double (*)(int))->double
у кого-нибудь есть идея о том, как это исправить или как обойти это? Я использую компилятор intel icpc (ICC) 13.1.2 с с std=с++11
спасибо
5 ответов
есть несколько вещей, чтобы уточнить ответ на ваш вопрос. Первый из которых что такое лямбда?
лямбда-выражение-это простое выражение, из которого компилятор будет генерировать уникальный тип, который не может быть назван, и в то же время он будет генерировать экземпляр типа. Когда пишешь:[](int i) { std::cout << i; }
компилятор создаст для вас тип, который примерно:
struct __lambda_unique_name {
void operator()(int i) const { std::cout << i; }
};
как вы можете видеть, это не a функция, но тип, который реализует operator()
как const
функции-члена. Если лямбда сделала какой-либо захват, компилятор будет генерировать код для захвата значения/ссылок.
в качестве углового случая для лямбд, подобных приведенным выше, где нет состояния, захваченного, язык допускает преобразование из типа лямбда в указатель на функцию с сигнатурой operator()
(за вычетом this
часть), поэтому лямбда выше, может быть неявно преобразован в указатель на функцию принимая int
и ничего не возвращая:
void (*f)(int) = [](int i) { std::cout << i; }
теперь, когда основы были изложены, в коде у вас есть эта лямбда:
auto f = [x,y](double (func)(int)) -> double {func(0); return 0.0;};
правила для параметров функций (которые также применяются к lambdas) определяют, что аргумент не может иметь тип функции, поэтому аргумент лямбда распадается на указатель на функцию (так же, как аргумент типа array распадается на тип указателя):
auto f = [x,y](double (*func)(int)) -> double {func(0); return 0.0;};
на более позднем этапе вы пытаются передать лямбду, которая имеет захват в качестве аргумента. Поскольку есть захват, специальное правило не применяется, и лямбда не преобразуется в указатель на функцию, дающую ошибку компилятора, которую вы видите.
в текущем стандарте, вы можете пойти одним из двух способов. Вы можете использовать type-erasure для удаления точного типа вызываемой сущности из подписи:
auto f = [x,y](std::function<double(int)> func) -> double {func(0); return 0.0;};
, потому что std::function<double(int)>
может быть инициализирован с вызвать сущность с соответствующей подписью, Это будет принимать lambdas в коде ниже, за счет стирания типа, которое обычно подразумевает динамическое распределение и динамическую отправку.
кроме того, вы можете отбросить синтаксический сахар и свернуть первый лямбда-эквивалент вручную, но сделать его общим. В этом случае, когда лямбда проста, это может быть допустимым вариантом:
struct mylambda {
template <typename F>
double operator()(F fn) const {
fn(0); return 0.0;
}
} f;
// then use the non-lambda as you tried:
f([x](int i) -> double {return 0.0;});
наконец, если вы достаточно терпеливы, вы можете подождать C++14, где (скорее всего, это еще не ратифицирована) будет поддержка полиморфные лямбды которые упрощают создание вышеуказанного класса:
auto f = [](auto fn) { fn(0.0); return 0.0; } // unrolls to 'mylambda' above
попробуйте использовать std:: function:
#include <functional>
int main()
{
int x=0;
int y=0;
auto f = [x,y](std::function<double(int)> func) -> double
{func(0); return 0.0;};
f([x](int i) -> double {return 0.0;});
}
возможно, вам придется просто укусить пулю и реализовать свои собственные функторы, как мы делали в темные века:
struct F {
int x;
int y;
F(int x_, int y_) : x(x_), y(y_) {}
template <typename G>
double operator() (G&& g) const {
g(0);
return 0.0;
}
};
#include <iostream>
int main()
{
int x = 0;
int y = 0;
auto f = F(x, y);
f([x](int i){return 0.0;});
f([](int i){std::cout << i << std::endl;});
}
Это должно поддерживать вас до тех пор, пока ваш компилятор не поддержит c++14 generic lambdas.
вы можете попробовать что-то вроде следующего, если вы знаете тип лямбды заранее, например:
int main()
{
int x = 0, y = 0;
auto f = [x]( int i )->double {
return (double)x;
};
auto f2 = [x,y]( decltype(f) func )->double {
return func( 0 );
};
f2( f );
return 0;
}
или альтернатива вы можете использовать <functional>
библиотека для более общего решения, например:
auto f = [x,y]( std::function<double(int)> func ) { /* Do stuff */ };
вы можете cify захвата лямбда, но это решение имеет свои ограничения:
#include <new>
#include <utility>
namespace
{
template <typename F, int I, typename L, typename R, typename ...A>
inline F cify(L&& l, R (*)(A...) noexcept(noexcept(
std::declval<F>()(std::declval<A>()...))))
{
static L l_(std::forward<L>(l));
static bool full;
if (full)
{
l_.~L();
new (static_cast<void*>(&l_)) L(std::forward<L>(l));
}
else
{
full = true;
}
return [](A... args) noexcept(noexcept(
std::declval<F>()(std::forward<A>(args)...))) -> R
{
return l_(std::forward<A>(args)...);
};
}
}
template <typename F, int I = 0, typename L>
inline F cify(L&& l)
{
return cify<F, I>(std::forward<L>(l), F());
}
int main()
{
int x=0;
int y=0;
auto f = [x,y](double (func)(int)) -> double
{func(0); return 0.0;};
f(cify<double(*)(int i)>([x](int i) -> double //works now
{return 0.0;}));
}
клик рабочий пример.