Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

How to return a std::function that contains a std::unique_ptr from a generalized lambda capture in C++14?

Tags:

c++

lambda

c++14

How do we return a std::function that contains a std::unique_ptr from a generalized lambda capture in C++14? Specifically, in the following code

// For std::function
#include <functional>

// For std::iostream
#include <iostream>

// For std::unique_ptr
#include <memory>

#if 0
std::function <void()> make_foo() {
    auto x = std::make_unique <int> (3);
    return [x=std::move(x)]() {
        std::cout << *x << std::endl;
    };
}
#endif

int main() {
    auto x = std::make_unique <int> (3);
    auto foo = [x=std::move(x)]() {
        std::cout << *x << std::endl;
    };
    foo();
}

Everything works fine when run with GCC 4.9.2 and C++14 turned on. Specifically, it shows that generalized lambda captures work. However, when we change the code where #if 1, we get the compile error:

g++ -g -std=c++14 test01.cpp -o test01
In file included from test01.cpp:4:0:
/usr/lib/gcc/x86_64-pc-linux-gnu/4.9.2/include/g++-v4/functional: In instantiation of 'static void std::_Function_base::_Base_manager<_Functor>::_M_clone(std::_Any_data&, const std::_Any_data&, std::false_type) [with _Functor = make_foo()::<lambda()>; std::false_type = std::integral_constant<bool, false>]':
/usr/lib/gcc/x86_64-pc-linux-gnu/4.9.2/include/g++-v4/functional:1914:51:   required from 'static bool std::_Function_base::_Base_manager<_Functor>::_M_manager(std::_Any_data&, const std::_Any_data&, std::_Manager_operation) [with _Functor = make_foo()::<lambda()>]'
/usr/lib/gcc/x86_64-pc-linux-gnu/4.9.2/include/g++-v4/functional:2428:19:   required from 'std::function<_Res(_ArgTypes ...)>::function(_Functor) [with _Functor = make_foo()::<lambda()>; <template-parameter-2-2> = void; _Res = void; _ArgTypes = {}]'
test01.cpp:17:5:   required from here
/usr/lib/gcc/x86_64-pc-linux-gnu/4.9.2/include/g++-v4/functional:1878:34: error: use of deleted function 'make_foo()::<lambda()>::<lambda>(const make_foo()::<lambda()>&)'
    __dest._M_access<_Functor*>() =
                                  ^
test01.cpp:15:27: note: 'make_foo()::<lambda()>::<lambda>(const make_foo()::<lambda()>&)' is implicitly deleted because the default definition would be ill-formed:
     return [x=std::move(x)]() {
                           ^
test01.cpp:15:27: error: use of deleted function 'std::unique_ptr<_Tp, _Dp>::unique_ptr(const std::unique_ptr<_Tp, _Dp>&) [with _Tp = int; _Dp = std::default_delete<int>]'
In file included from /usr/lib/gcc/x86_64-pc-linux-gnu/4.9.2/include/g++-v4/memory:81:0,
                 from test01.cpp:10:
/usr/lib/gcc/x86_64-pc-linux-gnu/4.9.2/include/g++-v4/bits/unique_ptr.h:356:7: note: declared here
       unique_ptr(const unique_ptr&) = delete;
       ^
Makefile:2: recipe for target 'all' failed
make: *** [all] Error 1

Now, given that the function we're returning contains a std::unique_ptr, it makes sense that we can't copy the resulting std::function. However, since we're returning a lambda function created on the fly, shouldn't this be an r-value and the definition valid? Basically, is there any way to fix make_foo where we still have a generalized lambda capture of a std::unique_ptr?

like image 816
wyer33 Avatar asked Jul 29 '15 04:07

wyer33


2 Answers

As @T.C. says in the comments, std::function requires that the callable it wraps be CopyConstructible, and your lambda isn't because of the unique_ptr data member.

You can make use of C++14's return type deduction for functions to return the lambda from make_foo and avoid wrapping it in std::function.

auto make_foo() {
    auto x = std::make_unique <int> (3);
    return [x=std::move(x)]() {
        std::cout << *x << std::endl;
    };
}

make_foo()();  // prints 3

Live demo

like image 112
Praetorian Avatar answered Sep 24 '22 06:09

Praetorian


std::function is needlessly copyable (how often do you honestly want to copy a std::function?). Due to type erasure, the fact that you can copy a std::function means that the stuff you store in a std::function must be copyable, even if you never do it!

You can implement a move-only std::function pretty easily. Getting good QOI is harder -- the SFO (small function optimization) similar to the SSO (small string optimization) would be a good idea to avoid a useless heap allocation.

here is a sketch of a move-only std::function called task. It has no SFO.

It is only needed if you need to store the resulting lambda in code "type-unconnected" to the code generating the lambda. If you do not have to do that, you can return auto and expose your implementation and let C++14 return type deduction do the work for you.

like image 36
Yakk - Adam Nevraumont Avatar answered Sep 21 '22 06:09

Yakk - Adam Nevraumont