How to capture a unique_ptr into a lambda expression? - c++

I have tried the following:
std::function<void ()> getAction(std::unique_ptr<MyClass> &&psomething){
//The caller given ownership of psomething
return [psomething](){
psomething->do_some_thing();
//psomething is expected to be released after this point
};
}
But it does not compile. Any ideas?
UPDATE:
AS suggested, some new syntax is required to explicitly specify we need to transfer the ownership to the lambda, I am now thinking about the following syntax:
std::function<void ()> getAction(std::unique_ptr<MyClass> psomething){
//The caller given ownership of psomething
return [auto psomething=move(psomething)](){
psomething->do_some_thing();
//psomething is expected to be released after this point
};
}
Would it be a good candidate?
UPDATE 1:
I will show my implementation of move and copy as following:
template<typename T>
T copy(const T &t) {
return t;
}
//process lvalue references
template<typename T>
T move(T &t) {
return std::move(t);
}
class A{/*...*/};
void test(A &&a);
int main(int, char **){
A a;
test(copy(a)); //OK, copied
test(move(a)); //OK, moved
test(A()); //OK, temporary object
test(copy(A())); //OK, copying temporary object
//You can disable this behavior by letting copy accepts T &
//test(move(A())); You should never move a temporary object
//It is not good to have a rvalue version of move.
//test(a); forbidden, you have to say weather you want to copy or move
//from a lvalue reference.
}

This issue is addressed by lambda generalized capture in C++14:
// a unique_ptr is move-only
auto u = make_unique<some_type>(some, parameters);
// move the unique_ptr into the lambda
go.run([u = move(u)]{do_something_with(u);});

You cannot permanently capture a unique_ptr in a lambda. Indeed, if you want to permanently capture anything in a lambda, it must be copyable; merely movable is insufficient.
This could be considered a defect in C++11, but you would need some syntax to explicitly say that you wanted to move the unique_ptr value into the lambda. The C++11 specification is very carefully worded to prevent implicit moves on named variables; that's why std::move exists, and this is a good thing.
To do what you want will require either using std::bind (which would be semi-convoluted, requiring a short sequence of binds) or just returning a regular old object.
Also, never take unique_ptr by &&, unless you are actually writing its move constructor. Just take it by value; the only way a user can provide it by value is with a std::move. Indeed, it's generally a good idea to never take anything by &&, unless you're writing the move constructor/assignment operator (or implementing a forwarding function).

The "semi-convoluted" solution using std::bind as mentioned by Nicol Bolas is not so bad after all:
std::function<void ()> getAction(std::unique_ptr<MyClass>&& psomething)
{
return std::bind([] (std::unique_ptr<MyClass>& p) { p->do_some_thing(); },
std::move(psomething));
}

A sub-optimal solution that worked for me was to convert the unique_ptr to a shared_ptr and then capture the shared_ptr in the lambda.
std::function<void()> getAction(std::unique_ptr<MyClass> psomething)
{
//The caller given ownership of psomething
std::shared_ptr<MyClass> psomethingShared = std::shared_ptr<MyClass>(std::move(psomething));
return [psomethingShared]()
{
psomethingShared->do_some_thing();
};
}

I used this really dodgy workaround, which involves sticking the unique_ptr inside a shared_ptr. This is because my code required a unique_ptr (due to an API restriction) so I couldn't actually convert it to a shared_ptr (otherwise I'd never be able to get my unique_ptr back).
My justification for using this abomination is that it was for my test code, and I had to std::bind a unique_ptr into the test function call.
// Put unique_ptr inside a shared_ptr
auto sh = std::make_shared<std::unique_ptr<Type>>(std::move(unique));
std::function<void()> fnTest = std::bind([this, sh, input, output]() {
// Move unique_ptr back out of shared_ptr
auto unique = std::move(*sh.get());
// Make sure unique_ptr is still valid
assert(unique);
// Move unique_ptr over to final function while calling it
this->run_test(std::move(unique), input, output);
});
Now calling fnTest() will call run_test() while passing the unique_ptr to it. Calling fnTest() a second time will result in an assertion failure, because the unique_ptr has already been moved/lost during the first call.

One also need to know, that lambdas capturing unique_ptr cannot be converted into std::function because std::function requires that the callable object is copyable.
auto lambdaWithoutCapture = [](){return 1;}; //Can be std::function
auto lambdaWithCapture = [=](){return 1;}; //Can be std::function
auto lambdaWithCapture2 = [&](){return 1;}; //Can be std::function
auto lambdaWithCapture3 = [uptrProblematic = std::move(uptrProblematic)]() mutable {return 1;}; //Can't be std::function
Therefore, if you don't have to specify return type of the function, you can use such approach which does not use std::function. But you need to know, that this will only work in local scope. You can't declare auto workerFactory(); in header file, as this will raise compilation error.
auto workerFactory()
{
std::unique_ptr uptrProblematic = std::make_unique<int>(9);
int importantData = 71;
return [=, uptrProblematic = std::move(uptrProblematic)](std::string input) mutable -> int {
std::cout << "Problematic variable is equal to: " << *uptrProblematic << "\n";
std::cout << "Important data is equal to: " << importantData << "\n";
std::cout << "Input equal to: " << input << "\n";
return 9;
};
}
int main()
{
auto worker = workerFactory();
worker("Test");
}

Related

C++ lambda, which was specified to capture by value, actually behaves as if it captures by reference

Looking at various examples with lambda expressions, I came across unexpected behavior for me. In this code, I expect that the variables captured by value will not change inside the lambda. Code execution shows the opposite. Can someone give an explanation. Thanks.
#include <iostream>
#include <functional>
using namespace std;
class Fuu{
public:
Fuu(int v):m_property(v){}
void setProperty(int v ){m_property = v;}
std::function<void(int)> lmbSet {[=](int v){ m_property = v;}};
std::function<void(char *)> lmbGet {[=](char * v){ cout<< v << " "<< m_property << endl;}};
std::function<void()> lmb4Static {[=]{ cout<<"static: "<< s_fild << endl;}};
void call() const {
lmbGet("test") ;
}
static int s_fild ;
int m_property;
};
int Fuu::s_fild = 10;
int main()
{
Fuu obj(3);
obj.call();
obj.setProperty(5);
obj.call();//expect 3
obj.lmbSet(12);
obj.call(); //expect 3
obj.lmb4Static();
Fuu::s_fild = 11;
obj.lmb4Static(); //expect 10
return 0;
}
That is a common problem. Common enough that they deprecated the implicit capture of this in a lambda with = for C++20. When your code is doing is:
std::function<void(int)> lmbSet {[=](int v){ m_property = v;}};
It is effectively doing this:
std::function<void(int)> lmbSet {[this](int v){ this->m_property = v;}};
The lambda captures the this pointer by copy, not m_property at all.
In general, but formally in C++20, the preferred way to do this would be to explicitly copy this (in which case, no = is necessary, though you could):
std::function<void(int)> lmbSet {[this](int v){ m_property = v;}};
To make a copy of the current object and modify that copy, you'd need to dereference this (and then the capture is a copy of *this) and to modify it, you'd need to mark your lambda mutable:
std::function<void(int)> lmbSet {[*this](int v) mutable { m_property = v;}};
It doesn't seem very useful, but perhaps that helps explain what's going on.
[Note: it's not valid to copy *this until the construction completes, or it's copying an object before its lifetime starts, and that will result in undefined behavior.]
Capture by value only takes copies of local variables (none in your example) and this pointer. Capturing this by value is equivalent to capturing non-static data members by reference. Static member variables, like globals, are not captured at all - you are accessing them directly instead.
If you actually need to keep a copy of non-local data, you can do it using "generalized capture" syntax which was added in C++14:
[m_property=m_property](char * v){ cout<< v << " "<< m_property << endl;}
In this case [=] effectively just copies this as a pointer. Then, when you access data members in your closure the copy of this is dereferenced, and if you modify that member it modifies the original member. If you think about it, it isn't possible that [=] copies each member. You would have an infinite recursion of copies as each lmbSet would make a new copy of lmbGet, which in turn would make a new copy of lmbSet.
Note that this solution requires that the rule of 3/5/0 be applied. If you try to make a copy of an instance of Fuu, the captured this pointer will copied and point to the original instance and not to the new copy. In general, capturing this or pointers to data members in default member initializes should be avoided.

Why would you explicitly move a forwarding reference?

I'm looking at some code, and I see the following function:
template <typename... Args>
static return_t make_return(Args &&... args)
{
// using std::forward<Args> will preserve lvalue args as such, but the point of this function
// is to make a return, where the 99.9+% case is moving a local (lvalue) into the return pack.
// Thus it forces a move, which will move `T&` args (but _not_ `const T&` args) into the
// return pack so that users don't need to type out a bunch of std::moves themselves/
// If they don't want implicit move they can just call std::make_tuple directly
return std::make_tuple(std::move(args)...);
}
The documentation here confuses me.
Why would you explicitly move a forwarding reference?
Wouldn't you want to preserve the lvalue / rvalue within a generic context?
I'm having trouble understanding the rationale or how this behavior would be different from the recommended std::forward.
To put it another way,
I've never seen anyone explicitly opt-out from perfectly-forwarding a forwarding reference.
Does it make sense?
Why would you explicitly move a forwarding reference?
Because it isn't being used for its forwarding properties. Yes, you are correct, we usually std::forward a forwarding reference. But in this case the author used forwarding references solely as a matter of convenience. If it was written as make_return(Args&... args) then it would be impossible to pass an rvalue to make_return, since a non-const lvalue reference may not bind to one.
By using forwarding references, the author allows passing values of any value category into the function, without incurring extra copies if none are needed. The documentation is there to clarify that the function signature isn't for forwarding, but simply for binding to whatever arguments it's given to move out.
A small example reveals the author's intent:
#include <tuple>
#include <iostream>
struct A {
A() { }
A(const A&) { std::cout << "copy\n"; }
A(A&&) { std::cout << "move\n"; }
};
template <typename Arg>
static std::tuple<A> make_return(Arg&& arg) {
return std::make_tuple(std::move(arg));
}
void f(const std::tuple<A>&) { }
void f1() {
std::cout << "return local via make_tuple: ";
A a{};
f(std::make_tuple(a));
}
void f2() {
std::cout << "return local via make_tuple(move): ";
A a{};
f(std::make_tuple(std::move(a)));
}
void f3() {
std::cout << "return local via make_return: ";
A a{};
f(make_return(a));
}
void f4() {
std::cout << "return const via make_tuple: ";
const A a{};
f(std::make_tuple(a));
}
void f5() {
std::cout << "return const via make_tuple(move): ";
const A a{};
f(std::make_tuple(std::move(a)));
}
void f6() {
std::cout << "return const via make_return: ";
const A a{};
f(make_return(a));
}
int main() {
f1();
f2();
f3();
f4();
f5();
f6();
}
Output:
return local via make_tuple: copy
return local via make_tuple(move): move
return local via make_return: move
return const via make_tuple: copy
return const via make_tuple(move): copy
return const via make_return: copy
In cases where a local non-const variable is returned, we want to std::move its contents. This is achievable using std::make_tuple(std::move(a)), because a plain std::make_tuple(a) would copy. To save some typing, the author wrote make_return as a shorthand for std::make_tuple(std::move(a)): the example shows that f3 works just like f2.
When a constant is passed, std::move won't make any difference, but no harm either. So we could use std::make_tuple, but make_return works just fine, too. Cases f4, f5, f6 all behave the same, showing that one doesn't really need to think twice before mixing constants and non-constants in make_return (in the case of multiple entries constituting return_t).
What remains is moving a non-const variable that is not local to the function, and thus we wouldn't like to destroy its contents. In these cases make_return is unwanted and one would need to resort back to manual invocation of std::make_tuple (utilizing std::move where appropriate only).
Now what would this look like with std::forward? Changing the definition of make_return to utilizing
std::make_tuple(std::forward<Arg>(arg));
produces:
return local via tuple: copy
return local via tuple(move): move
return local via make_return: copy
return const via tuple: copy
return const via tuple(move): copy
return const via make_return: copy
since a in f3 gets passed as a const A&. Indeed, make_return is then, by the logic of forwarding, a mere synonyme for std::move, losing any benefit we hoped to achieve.
make_return() is to return a tuple using a value, since this value won't be needed anymore, as is used in make_return (end scope of the a function), there's no need to use std::forward<>, since it could forward lvalue references which incurs in copies (depending on implementation), but value is at the end of scope so is not needed to preserve any resources.
Forcing std::move on the make_tuple, forces to use rvalue references first, omitting possible more overhead (depending on implementation).

To move, or not to move from r-value ref-qualified method?

In the following C++11+ code which return statement construction should be preferred?
#include <utility>
struct Bar
{
};
struct Foo
{
Bar bar;
Bar get() &&
{
return std::move(bar); // 1
return bar; // 2
}
};
Well, since it's a r-value ref qualified member function, this is presumably about to expire. So it makes sense to move bar out, assuming Bar actually gains something from being moved.
Since bar is a member, and not a local object/function parameter, the usual criteria for copy elision in a return statement don't apply. It would always copy unless you explicitly std::move it.
So my answer is to go with option number one.
I prefer option 3:
Bar&& get() &&
// ^^
{
return std::move(bar);
}
and, while we're at it:
Bar& get() & { return bar; }
Bar const& get() const& { return bar; }
Bar const&& get() const&& { return std::move(bar); }
We're an rvalue, so it should be free to cannibilize our resources, so move-ing bar is correct. But just because we're open to moving bar doesn't mean we have to mandate such a move and incur extra operations, so we should just return an rvalue reference to it.
This is how the standard library do - e.g. std::optional<T>::value.
I would like to clarify my point (from comments). Even though moving result should in general be considerably more efficient than copying, it is not my primary concern here. The core issue arises from false assumption that by calling this method on r-value reference to Foo instance caller's intentions include creation of a new Bar value. For example:
Foo Produce_Foo(void);
// Alright, caller wanted to make a new `Bar` value, and by using `move`
// we've avoided a heavy copy operation.
auto bar{Produce_Foo().get()};
// Oops! No one asked us to make a useless temporary...
cout << Produce_Foo().get().value() << endl;
The solution would be to add a dedicated functions to be used just to take a peek at stored bar and to take control over content of stored bar object.
Bar const & get_bar() const noexcept
{
return bar;
}
// no particular need to this method to be r-value reference qualified
// because there is no direct correlation between Foo instance being moved / temp
// and intention to take control over content of stored bar object.
Bar give_bar() noexcept
{
return ::std::move(bar);
}
Now that user has a choice there will be no more problems:
// Alright, caller wanted to make a new `Bar` value, and by using `move`
// we've avoided a heavy copy operation.
// There is also no need to figure out whether Produce_Foo returned an rvalue or not.
auto bar{Produce_Foo().give_bar()};
// Alright, no extra temporaries.
cout << Produce_Foo().get_bar().value() << endl;
As for use cases for r-value reference qualified methods, I think they are mostly useful when dealing with temporaries of the same type as this object. e.g. string class implementing such concatenation operator can reduce amount of
reallocations, essentially performing like a dedicated string builder.

Spamming std::move is the way to go?

About std::move, here is what I can interpret, according to http://en.cppreference.com/w/cpp/utility/move :-
If I want to transfer ownership, I have to call std::move (or in rare case, std::forward).
Responsibility of std::move is calling operator=(A&& other).
The most essential step of the move operation is supposed to be implemented in operator=(A&&).
It is tricky to ensure that operator=(A&&) would be called. It need a special converter.
There are only two converters in the C++ world that can convert variables into xvalue (the &&) : std::move and std::forward.
Question
After adding many of std::move(std::unique_ptr) in my code, I start to worry that for such basic feature like transfer ownership, I have to heavily rely on the standard library (std::).
Do I really have to use std::move to transfer ownership?
Is spamming and hard-code calling std::move in many places of code-base a correct way to go for a high-standard program?
Should std::move be encapsulated?
They are actually a single question, but ask in different perspectives.
Edit
As request, here is my trial & error. It compiled ok.
I have no problem about the code, but I worry about its approach / pattern.
https://ideone.com/y8Pcgf
class T{
public: int value;
public: T(int a=1234){
value = a;
}
};
int main() {
std::unique_ptr<T> t1 = std::unique_ptr<T>(new T(1));
void* databaseNew=operator new [](sizeof(std::unique_ptr<T>));
std::unique_ptr<T>* t1ptr=static_cast<std::unique_ptr<T>*>(databaseNew);
new (t1ptr) std::unique_ptr<T>(std::move(t1));
return 0;
}
Rule of thumb:
If you're in a deduced x-value context, use std::forward:
template<class T>
void foo(T&& t) // T is deduced x-value, so we forward it
{
bar(std::forward<T>(t));
}
Otherwise use std::move
template<class T>
void foo1(std::vector<T> v) // although vector<T> is deduced, it's not an x-value
{
bar(std::move(v)); // so move it
}
template<class T>
void foo2(std::vector<T>&& v) // although vector<T> is deduced, it's not an x-value.
// In this case an r-value reference
{
bar(std::move(v)); // so move it
}
template<class T>
void foo3(std::vector<T>& v) // although vector<T> is deduced, it's not an x-value.
// In this case an l-value reference
{
bar(std::move(v)); // so move it
}
void foo4(std::vector<int> v) // complete type
{
bar(std::move(v)); // so move it
}
void foo5(std::vector<int> const & v) // const reference
{
bar(v); // not much point in moving it. std::move would cast it
// to std::vector<int> const&&, which although is detectable
// decays to std::vector<int> const&
}
which although is detectable... what?
It is permissible, if not necessarily advisable to write code like this:
#include <iostream>
struct X
{
void foo() const &
{
// do one thing...
std::cout << "one thing\n";
}
void foo() const &&
{
// do something else...
std::cout << "or another\n";
}
};
int main()
{
const X x;
x.foo();
std::move(x).foo();
}
const r-value references do exist, it's just that no-one uses them because there is no reasonable use-case.
The need to explicitly move, of which you complain, was actually done on purpose. Before unique_ptr, STL had a horrid construct called auto_ptr. It would move ownership impllicitly, and was borderline unusable unless you really really really knew what you were doing.
To make things more usable, in most cases C++ now requires you to explicitly state that you intend on moving ownership over a container, by using std::move.
In fact, std::move is little more than a cast to an rvalue reference.
There are cases where such an explicit specification is not necessary. For example, if the container from which you take ownership is already an rvalue (e.g. - a temporary object), then no case using std::move is necessary. For example, the following doesn't compile:
std::unique_ptr<int> a;
a = new int;
But the following does, without needing a move:
std::unique_ptr<int> a;
a = std::unique_ptr<int>(new int);
The reason this does not need a call to std::move, despite invoking the move operator, is that the object we move the ownership away from is already a temporary object (i.e. - an rvalue), so no cast is necessary.
Another example is if you call a function that returns a unique_ptr. You might have to call std::move inside the function to get it into the return value, but you do not need to call std::move on the function's return value to get it into the outside unique_ptr. It is already an rvalue, and therefor no cast is necessary.

Moving semantics of unique_ptr

Lets consider the following piece of code:
template<typename T>
void f(std::unique_ptr<T>&& uptr) { /*...*/ }
In another function:
void g()
{
std::unique_ptr<ANY_TYPE> u_ptr = std::make_unique<ANY_TYPE>();
f(std::move(u_ptr));
X: u_ptr->do_sth(); // it works, I don't understand why. Details below.
}
I don't understand why u_ptr in line X is still alive.
After all I forced him to be moved (std::move).
---EDIT---
Ok, so now:
The code is still working:
class T{
public:
T(){}
void show(){
std::cout << "HEJ!\n";
}
};
void f(std::unique_ptr<T> ref){
ref->show();
}
int main()
{
std::unique_ptr<T> my;
my->show();
f(std::move(my));
my->show(); // How is it possible. Now, f takes unique_ptr by value
return 0;
}
You didn't show us that code to function f, but presumably it didn't move the pointer, even though it had permission to.
You passed the unique_ptr by reference. If function invocation actually moved it, then the function couldn't use it because it would be gone before the function had a chance to.
If you want function invocation to actually move the pointer, you need to pass the pointer by value, not be reference. That value would be a unique_ptr for it to be moved into. In that case, you should declare the function as taking a std::unique_ptr<T> instead of a std::unique_ptr<T>&&. Then you can actually invoke the move constructor when you call the function.
Update: With your latest change, the unique_ptr would no longer reference any valid object due to the move construction. You just never check that it does. Invoking a non-virtual method that doesn't access any member variables can work just the same whether the object is valid or destroyed because it doesn't need anything from the object. You also never made the unique_ptr actually point to anything.
Instead, make the unique_ptr point to something. After it's moved, try calling a virtual function or accessing a member whose value is changed by the destructor. Like this:
#include <iostream>
#include <memory>
class T{
public:
T() : valid (true) {}
~T() { valid = false; }
bool valid;
void show(){
std::cout << "HEJ! " << valid << std::endl;
}
};
void f(std::unique_ptr<T> ref){
ref->show();
}
int main()
{
std::unique_ptr<T> my (new T); // Make it point to a new object
my->show();
f(std::move(my));
my->show(); // Try to access
return 0;
}
in the line f(std::unique_ptr<T>&& uptr) uptr is not an object - it's a reference. a reference which capable to catch temporeries and mutate them.
it's like asking why doesn't the object get cloned in the next example
void func(std::string& str);
std::string str_ = "yyy";
func(str_);
str_ is passed by "regular" reference and won't get copied - this is what pass by reference means.
std::move only cast l-value to r-value-reference, which uptr in f(std::unique_ptr<T>&& uptr) can reference, it's a reference referencing an object. opposed to the common conception, std::move won't do any moving by itself, only casts the object to r-value-reference for the move constructor/assg. operator to kick in.
here, the pointer still holds valid data since it was not moved, only casted to r-value-reference.
if you want the object to move you have to declare the parameter as object, not reference : f(std::unique_ptr<T> uptr)
In your edit, you have undefiend behaviour, so everything may occure.
The reason why your call to show doesn't crash is because it doesn't use the this pointer (it doesn't try to modify or access a data member).
Try this:
class T{
public:
int v;
T(){}
void show(){
v = 0;
std::cout << "HEJ!\n";
}
};
void f(std::unique_ptr&& ref)
This is the answer when you initially had your f function taking a rvalue reference &&.
Your function takes a rvalue reference. Therefore, no new unique_ptr object is created yet, you are simply passing a reference.
Inside your f function, if you create a a local unique_ptr, with the parameter uptr, then finally uptr will be moved to create that new object.
template<typename T>
void f(std::unique_ptr<T>&& uptr)
{
//move uptr into local_unique_ptr
//note that we have to use move again
//because uptr has a name, therefore its a lvalue.
auto local_unique_ptr = std::unique_ptr<T>(std::move(uptr));
}
The important thing to always know is that std::move is simply a static_cast.
If you pass a lvalue to std::move, it returns a rvalue. If you pass a rvalue, it returns a rvalue. That's it.
Your function f may not in fact move the pointer. Merely taking an object by && does not modify the object.
u_ptr->do_sth() may invoke a static member function or a member function that does not access the object (this) and this is why it does not crash.