Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Forward a function into a lambda-expression in c++11

Tags:

c++

c++11

lambda

For a Packaged_Task implementation in C++11 i want to achieve what i've expressed in C++14 Code below. In other words i want to forward into a lambda expression.

template<class F>
Packaged_Task(F&& f) {
    Promise<R> p;
    _future = p.get_future();

     auto f_holder = [f = std::forward<F>(f)]() mutable { return std::move(f); };
 ///...

I'm aware of workarounds for moving into a lambda (but unfortenately this workarounds need a default constructible Object, in my case the object is most often a lambda expression without default-constructor)

like image 862
user1235183 Avatar asked Jun 15 '16 06:06

user1235183


People also ask

What is the correct syntax for lambda expression in C++11?

Lambdas can both capture variables and accept input parameters. A parameter list (lambda declarator in the Standard syntax) is optional and in most aspects resembles the parameter list for a function. auto y = [] (int first, int second) { return first + second; };

Can you call a function in a lambda?

The whole lambda function lambda x : x * x is assigned to a variable square in order to call it like a named function. The variable name becomes the function name so that We can call it as a regular function, as shown below. The expression does not need to always return a value.

Is there lambda function in C?

Significance of Lambda Function in C/C++ Lambda Function − Lambda are functions is an inline function that doesn't require any implementation outside the scope of the main program. Lambda Functions can also be used as a value by the variable to store.


3 Answers

How about creating a wrapper struct which does a move during copy construction:(. (I know that's bad, makes me remember of auto_ptr)

template <typename F>
struct Wrapped {
  using Ftype = typename std::remove_reference<F>::type;
  Wrapped(Ftype&& f): f_(std::move(f)) {}

  Wrapped(const Wrapped& o): f_(std::move(o.f_)) {}

  mutable Ftype f_; 
};

template<class F>
Packaged_Task(F&& f) {
    Promise<R> p;
    _future = p.get_future();
     Wrapped<std::remove_reference<decltype(f)>::type> wrap(std::forward<F>(f));

     auto f_holder = [wrap]() mutable { return std::move(wrap.f_); };

This is just a rough idea. Not compiled or tested.

NOTE: I have seen this technique before, do not remember whether it was on SO itself or on some blog.

like image 95
Arunmu Avatar answered Oct 12 '22 05:10

Arunmu


First, let's boil down the question to its core: the function object is somewhat of a distraction. Essentially, you want to be able to create a lambda with a capture holding a move-only object. In C++11 that isn't directly supported which gave raise to the C++14 approach of allowing specification of how the capture is build.

For C++11 it is necessary to use a copy. Since the underlying type doesn't support copying, it become necessary to actually move the object instead of copying it. Doing so can be achieved by a suitable wrapper defining a copy constructor not really copying but rather moving. Here is an example showing that:

#include <utility>

struct foo {
    foo(int) {}
    foo(foo&&) = default;
    foo(foo const&) = delete;
};

template <typename T>
class move_copy
{
    T object;
public:
    move_copy(T&& object): object(std::move(object)) {}
    move_copy(move_copy& other): object(std::move(other.object)) {}

    T extract() { return std::forward<T>(this->object); }
};

template <typename T>
void package(T&& object)
{
    move_copy<T> mc(std::forward<T>(object));
    foo g = [mc]() mutable { return mc.extract(); }();
}

int main()
{
    foo f(0);
    package(std::move(f));
}

The move_copy<T> wrapper actually just captures the argument the way it is passed: if an lvalue is being passed in, an lvalue is captured. To properly get hold of the contained object, the extract() member std::forward<T>()s the object: the function can be called only once safely as an object is potentially moved from there.

like image 26
Dietmar Kühl Avatar answered Oct 12 '22 05:10

Dietmar Kühl


Breaking copy semantics by making it a move is a bad idea. If it was the only option, go for it, but it is not.

Instead, we can pass the moved value in as an argument to the lambda, and move it into the wrapping code.

curry_apply takes some value and a function object, and returns that function object with the value bound to the first argument.

template<class T, class F>
struct curry_apply_t {
  T t;
  F f;
  template<class...Args>
  auto operator()(Args&&...args)
  -> typename std::result_of_t<F&(T&, Args...)>::type
  {
    return f(t, std::forward<Args>(args)...);
  }
};
template<class T, class F>
curry_apply_t<typename std::decay<T>::type, typename std::decay<F>::type>
curry_apply( T&& t, F&& f ) {
  return {std::forward<T>(t), std::forward<F>(f)};
}

Use:

template<class F>
Packaged_Task(F&& f) {
  Promise<R> p;
  _future = p.get_future();

  auto f_holder = curry_apply(
    std::move(_future),
    [](Future<R>& f) mutable { return std::move(f); };
  );

basically we store the moved-in data outside of the lambda in a manually written function object. We then pass it in as an lvalue argument at the front of the lambda's argument list.

Here is a more complex version of the same solution.

like image 38
Yakk - Adam Nevraumont Avatar answered Oct 12 '22 04:10

Yakk - Adam Nevraumont