Я хотел бы сделать что-то вроде этого:
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 = c ++ 11
Спасибо
Есть несколько вещей, которые нужно уточнить по вашему вопросу. Первый из которых что такое лямбда?
Лямбда-выражение — это простое выражение, из которого компилятор сгенерирует уникальный тип, который не может быть назван, и в то же время сгенерирует экземпляр типа. Когда вы пишете: [](int i) { std::cout << i; }
компилятор сгенерирует для вас тип примерно:
struct __lambda_unique_name {
void operator()(int i) const { std::cout << i; }
};
Как видите, это не функция, но тип, который реализует 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;};
Правила для параметров функций (которые также применяются к лямбдам) определяют, что аргумент не может иметь тип функция, таким образом, аргумент лямбды распадается на указатель на функцию (так же, как аргумент массива типов распадается на тип указателя):
auto f = [x,y](double (*func)(int)) -> double {func(0); return 0.0;};
Позже вы пытаетесь передать лямбду, в качестве аргумента которой используется захват. Поскольку есть перехват, специальное правило не применяется, и лямбда не конвертируется в указатель на функцию, которая выдает ошибку компилятора, которую вы видите.
В текущем стандарте вы можете пойти одним из двух способов. Вы можете использовать тип-стирание, чтобы удалить точный тип вызываемой сущности из подписи:
auto f = [x,y](std::function<double(int)> func) -> double {func(0); return 0.0;};
Потому что std::function<double(int)>
может быть инициализирован с любым подлежащий выкупу объект с соответствующей подписью, он будет принимать лямбда-выражения в приведенном ниже коде за счет стирания типа, что обычно подразумевает динамическое распределение и динамическую диспетчеризацию.
Кроме того, вы можете отбросить синтаксический сахар и свернуть первый лямбда-эквивалент вручную, но сделать его универсальным. В этом случае, если лямбда проста, это может быть допустимым вариантом:
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.
Например, вы можете попробовать что-то вроде следующего, если заранее знаете тип лямбды:
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 */ };
Вы можете указать захват лямбда, но это решение имеет свои ограничения:
#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;}));
}
Нажмите для рабочего примера.