Передать лямбда-выражение в лямбда-аргумент 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;}));
}

клик рабочий пример.