Создание экземпляра С++ lambda по типу - программирование
Подтвердить что ты не робот

Создание экземпляра С++ lambda по типу

Мне нужен способ сделать функтор функцией. Теперь я пытаюсь связать вызов функции с помощью лямбда-функции и создать ее позже. Но компилятор говорит, что конструктор lambda удален. Так есть ли способ скомпилировать этот код? Или, может быть, еще один способ для этого?

#include <iostream>  

void func()
{
    std::cout << "Hello";
}

auto t = []{ func(); };
typedef decltype(t) functor_type;

template <class F>
void functor_caller()
{
    F f;
    f();
}

int main()
{
    functor_caller<functor_type>();
    return 0;
}

Теперь я получаю такую ​​ошибку компилятора:

error: use of deleted function  '<lambda()>::<lambda>()'

error: a lambda closure type has a deleted default constructor

По-моему, единственный способ - использовать макрос:

#define WRAP_FUNC(f) \
struct f##_functor       \
{                       \
    template <class... Args >                             \
    auto operator()(Args ... args) ->decltype(f(args...)) \
    {                                                     \
        return f(args...);                                \
    }                                                     \
};

то

WRAP_FUNC(func);

а затем (в основном)

functor_caller<func_functor>()
4b9b3361

Ответ 1

Код не имеет смысла. Представьте, что у вас есть захватывающая лямбда:

{
    int n = 0;
    auto t = [&n](int a) -> int { return n += a; };
}

Что это может означать по умолчанию - построить объект типа decltype(t)?

Как предлагает @Matthieu, вы можете обернуть лямбда в объект function:

std::function<int(int)> F = t;

Или вы можете создать шаблон своего сайта непосредственно по типу лямбда (или любого вызываемого объекта):

template <typename F>
int compute(int a, int b, F f)
{
    return a * f(b);  // example
}

Использование: int a = 0; for (int i : { 1, 3, 5 }) { a += compute(10, i, t); }

Если это вообще возможно, предпочтительнее второй стиль, поскольку преобразование в std::function является нетривиальной, потенциально дорогостоящей операцией, а также фактическим вызовом функции через результирующий объект. Однако, если вам нужно сохранить единый набор гетерогенных вызываемых объектов, то std::function может быть самым простым и удобным решением.

Ответ 2

У Lambdas нет конструкторов по умолчанию. Когда-либо. Единственные конструкторы, к которым они иногда дают доступ, - это (в зависимости от того, что они захватывают) конструкторы копирования и/или перемещения.

Если вы создаете функтор без открытого конструктора по умолчанию, вы получите ту же ошибку.

В С++ 17 вы можете решить эту проблему с помощью constexpr lambda и operator+ для указателя на разложение. Тип, который содержит указатель на функцию и вызывает его легко с параметрами шаблона auto.

В С++ 11 вам нужно немного взломать.

template<class F>
struct stateless_lambda_t {
  static std::aligned_storage_t< sizeof(F), alignof(F) >& data() {
    static std::aligned_storage_t< sizeof(F), alignof(F) > retval;
    return retval;
  };
  template<class Fin,
    std::enable_if_t< !std::is_same< std::decay_t<Fin>, stateless_lambda_t >{}, int> =0
  >
  stateless_lambda_t( Fin&& f ) {
    new ((void*)&data()) F( std::forward<Fin>(f) );
  }
  stateless_lambda_t(stateless_lambda_t const&)=default;
  template<class...Args>
  decltype(auto) operator()(Args&&...args)const {
    return (*static_cast<F*>( (void*)&data() ))(std::forward<Args>(args)...);
  }
  stateless_lambda_t() = default;
};
template<class F>
stateless_lambda_t<std::decay_t<F>> make_stateless( F&& fin ) {
  return {std::forward<F>(fin)};
}

Теперь мы можем:

auto t = make_stateless([]{ func(); });

и ваш код работает.

A static_assert или SFINAE, что F - фактически пустой тип, может быть хорошей идеей. Знаете, для качества.

Использование функций С++ 14 можно заменить на ручные decltype и spewing typename и ::type ключевые слова. Этот ответ был первоначально написан для вопроса С++ 14, который был закрыт как дубликат этого.

живой пример.

Ответ 3

Мы можем предположить, что lambda всегда будет пустым, поэтому мы можем просто сделать приведение из другого пустого типа, поскольку оба они имеют одинаковый макет памяти. Таким образом, мы создаем класс-оболочку, который создает конструктивный функциональный объект по умолчанию:

template<class F>
struct wrapper
{
    static_assert(std::is_empty<F>(), "Lambdas must be empty");
    template<class... Ts>
    auto operator()(Ts&&... xs) const -> decltype(reinterpret_cast<const F&>(*this)(std::forward<Ts>(xs)...))
    {
        return reinterpret_cast<const F&>(*this)(std::forward<Ts>(xs)...);
    }
};

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

Наконец, лямбда могла быть построена следующим образом:

template <class F>
void function_caller()
{
    wrapper<F> f;
    f();
}

Ответ 4

Нет.

Однако я считаю, что lambdas можно скопировать, поэтому ваш functor_caller может принять аргумент для инициализации его атрибута.

Тем не менее, вместо того, чтобы изобретать колесо, я использовал вместо него std::function.