When using std::move with a lambda when does the move happen - c++

If I create a lambda in a function and capture a variable to the lambda using std::move, when does the move happen? Is it when the lambda is created or when the lambda is executed?
Take the following code for example ... when do the various moves happen? Is it thread safe if myFunction is called on one thread and testLambda is executed on another thread?
class MyClass {
private:
// Only accessed on thread B
std::vector<int> myStuff;
// Called from thread A with new data
void myFunction(const std::vector<int>&& theirStuff) {
// Stored to be called on thread B
auto testLambda = [this, _theirStuff{ std::move(theirStuff) }]() {
myStuff = std::move(_theirStuff);
};
// ... store lambda
}
// Elsewhere on thread A
void someOtherFunction() {
std::vector<int> newStuff = { 1, 2, .... n };
gGlobalMyClass->myFunction(std::move(newStuff));
}

If I create a lambda in a function and capture a variable to the lambda using std::move, when does the move happen? Is it when the lambda is created or when the lambda is executed?
If you had written what I believe you intended to write, then the answer would be: both. Currently, the answer is: neither. You have a lambda capture _theirStuff { std::move(theirStuff) }. This basically declares a member of the closure type, which will be initialized when the closure object is created as if it were
auto _theirStuff { std::move(theirStuff) };
You also have
myStuff = std::move(_theirStuff);
in the lambda body.
However, your parameter theirStuff is actually an rvalue reference to a const std::vector<int>. Thus, _theirStuff { std::move(theirStuff) } is not actually going to perform a move, because a const std::vector cannot be moved from. Most likely, you wanted to write std::vector<int>&& theirStuff instead. Furthermore, as pointed out by #JVApen in the comments below, your lambda is not mutable. Therefore, _theirStuff will actually be const as well, and, thus, also cannot be moved from. Consequently, your code above, despite all the std::move, will actually make a copy of the vector every time. If you had written
void myFunction(std::vector<int>&& theirStuff)
{
auto testLambda = [this, _theirStuff { std::move(theirStuff) }]() {
myStuff = std::move(_theirStuff);
};
}
You would be moving theirStuff into _theirStuff when the closure object is created. And you would be copying _theirStuff into myStuff when the lambda is called. If you had written
void myFunction(std::vector<int>&& theirStuff)
{
auto testLambda = [this, _theirStuff { std::move(theirStuff) }]() mutable {
myStuff = std::move(_theirStuff);
};
}
Then you would be moving theirStuff into _theirStuff when the closure object is created. And you would be moving _theirStuff into myStuff when the lambda is called. Note that, as a consequence, your lambda then cannot really be called twice. I mean, it can, but it will only really work once since _theirStuff will be empty after the first time the lambda is called…
Also, note that above description is only valid for the particular combination of types in your example. There is no general definition of what it actually means to move an object. What it means to move an object is entirely up to the particular type of the object. It may not even mean anything. std::move itself does not really do anything. All it does is cast the given expression to an rvalue reference. If you then initialize another object from the result of std::move, or assign the result to an object, overload resolution will pick a move constructor or move assignment operator—if one exists—instead of the normal copy constructor or copy assignment operator. It is then up to the implementation of the move constructor/move assignment operator of the respective type to actually perform a move, i.e., do whatever it is that's supposed to be done for the particular type in case of initialization or assignment from an rvalue. So, in a way, what you do when you apply std::move is that you advertise the respective object as "this may be moved from". Whether or not it actually will be moved from (and, if so, what that actually means) is up to the implementation. In the particular case of std::vector, the move constructor/move assignment operator, by definition, guarantee that not only the contents of the original vector will be taken over from the original object, but also that the original object will be empty afterwards. In many other cases, it may be undefined behavior to do anything with an object that was moved from (except, maybe, destroy it; that one can be pretty much taken for granted as a type that doesn't at least allow that would be pretty much useless; typically, you will at least be able to assign a new value to an object that was moved from, but even that is not guaranteed in general). You always have to check for the particular type at hand what condition an object is guaranteed to be in after having been moved from…

Related

C++ class member rvalue assignment

I have seen C++ code that assigns a class member using an std::move call on an rvalue as follows:
class Widget {
std::vector<int> m_data{};
public:
// 1. OK
// x contents are copied locally, m_data space is extended if necessary
void set_data(const std::vector<int>& x) {
m_data = x;
}
// 2. x creates an rvalue that is moved into m_data. What if Widget is dynamically allocated?
void set_data(std::vector<int> x) {
m_data = std::move(x);
}
// 3. x is an rvalue generated before the call. What if Widget is dynamically allocated?
void set_data(std::vector<int>&& x) noexcept {
m_data = std::move(x);
}
};
Widget* pW = new Widget{};
pW->setData(std::vector<int>{1, 2, 3});
I don't understand 2 and 3. How is it possible to safely move an rvalue to a class member if the scope of Widget is not the same than the rvalue passed to set_value()?
EDIT: Fixed my code after user17732522 pointed out that the original version was passing an lvalue, not an rvalue to pW->setData();
std::move does not affect the lifetime of the moved object itself. Instead it indicates that ownership over resources owned by the object on which std::move is called may/should be taken over by the function to which std::move(/*...*/) is an argument.
For example for a std::vector moving the object means that the destination vector should take over any dynamic memory allocations made by the source vector containing the vector elements, so that the source vector's state after the move will be that of an empty vector and the state of the destination vector will be that of the source before the move without any allocation or copying of elements needing to take place. The vector objects themselves are not otherwise affected.
std::move on a type that doesn't own any resources, e.g. a simple std::pair<int, float>, has no effect. It will simply result in a copy as without move semantics.
Whether the object is instantiated in dynamic scope, or not, is completely immaterial as far as move semantics getting used, or not, when passing parameters for an invokation of the object's method, and whether or not the object's methods use move semantics inside the method. It is completely irrelevant.
Whether or not undefined behavior results from usage or non-usage of move semantisc will be determined by factors that are not necessarily relevant to the object's scope.
void set_data(std::vector<int> x) {
m_data = std::move(x);
}
In this case, the method caller's is responsible for using move semantics for set_data()'s parameter. Failure to do so will result in the parameter, presumably, getting copy-constructed. Whatever the case may be, it is immaterial when it comes to move-assignment that takes place when assigning m_data from the passed-in parameter. This will happen irregardless of how set_data() gets invoked. The method has no knowledge of how the parameter got passed in.
void set_data(std::vector<int>&& x) noexcept {
m_data = std::move(x);
}
Here, the caller is responsible for producing a movable rvalue reference. Attempting to pass an lvalue, which would produce a loss of move semantics, results in ill-formed code. This, effectively, forces the caller into employing move semantics when calling this method.
How is it possible to safely move an rvalue to a class member if the scope of Widget is not the same than the rvalue passed to set_value()?
Again, it is immaterial. Whether Widgetexists in dynamic scope, or not, is immaterial as far as move semantics related to calling the object's methods. It may or may not indirectly affect whether or not the totality of the object's method calls results in undefined behavior, or not, but the move semantics themselves have no relation to the scope of the object itself.

Why can't I move an element into vector of non-copyables?

The compiler tells me I'm trying to access a deleted function (i.e. the copy constructor of a lambda expression). But I don't see where.
std::vector<std::function<void()>> tasks;
std::packaged_task<int()> task{ [] { return 1; } };
tasks.emplace_back(
[ t = std::move(task) ] () mutable { t(); });
(code is also here)
(I'm trying to find out why they use shared_ptr<task> in https://www.slideshare.net/GlobalLogicUkraine/c11-multithreading-futures).
On Gcc and MSVC I get the same error - I fear I'm doing something wrong...
error: use of deleted function
'main()::<lambda()>::<lambda>(const main()::<lambda()>&)'
Why can't I emplace this std::function onto the vector?
From cppreference:
F must meet the requirements of Callable and CopyConstructible
Where F is the function type used to construct the std::function. However, std::packaged_task is not copy constructible. Thus in the capture list, t is not copy constructible, and is a non-static member of the lambda, making the implicit copy constructor for the lambda deleted.
Short answer: Lambdas and std::packaged_task are not std::functions.
Long answer, you cannot move a std::packaged_task into a std::function
Here's what I'm offering as a solution:
std::vector<std::packaged_task<int()>> tasks;
std::packaged_task<int()> task{ [] () mutable { return 1; } };
tasks.emplace_back( std::move(task) );
If you actually need a std::function, and not just any callable, you'll have to bind a lambda into a std::function
The constructor of std::function requires the passed function object to be CopyConstructible, but std::packaged_task<F> is not (for any F). std::function performs type erasure where the dynamic type is not visible in the static type. Consider e.g.:
int invoke(std::function<int()> f) { return f(); }
int main()
{
std::packaged_task<int()> p{/*etc*/};
auto l = [] { return 5; };
std::function<int()> f( /* either p or l */ );
std::cout << invoke(f) << '\n';
}
The call to invoke requires copying f (pass by value). However, f is copyable if it has been made from l, but not copyable if it has been made from p, and this has nothing to do with the static type of f. There are basically three approaches to this problem:
Forbid copying of std::function at compile time.
Allow copying std::function at compile time, but throw a run time error if the contained type is not copyable.
Allow copying std::function at compile time, and require any function object that you put into it to be copyable.
Approach #1 is very restrictive on how functions may be stored, passed and shared, and basically forbids common use cases in favor of the uncommon case of using a non-copyable function object.
Approach #2 is problematic because users would need to be educated that copying a std::function may fail in some cases and use great diligence when writing their code. Also, if the design requires sharing functions they may need to be wrapped in a std::shared_ptr. And if they need to be copied and may be stateful, it becomes even worse.
No matter how you view approach #3, it is the one that was standardized. But in light of the above-mentioned problems, it is also easily to defend.
As a matter of fact, I have written a unique_function class template that uses approach #1 for my current project because for us the use case of storing non-copyable asynchronous task objects is quite common, and copying or sharing such a task isn't necessary.

Does D have a move constructor?

I am referencing this SO answer Does D have something akin to C++0x's move semantics?
Next, you can override C++'s constructor(constructor &&that) by defining this(Struct that). Likewise, you can override the assign with opAssign(Struct that). In both cases, you need to make sure that you destroy the values of that.
He gives an example like this:
// Move operations
this(UniquePtr!T that) {
this.ptr = that.ptr;
that.ptr = null;
}
Will the variable that always get moved? Or could it happen that the variable that could get copied in some situations?
It would be unfortunate if I would only null the ptr on a temporary copy.
Well, you can also take a look at this SO question:
Questions about postblit and move semantics
The way that a struct is copied in D is that its memory is blitted, and then if it has a postblit constructor, its postblit constructor is called. And if the compiler determines that a copy isn't actually necessary, then it will just not call the postblit constructor and will not call the destructor on the original object. So, it will have moved the object rather than copy it.
In particular, according to TDPL (p.251), the language guarantees that
All anonymous rvalues are moved, not copied. A call to this(this)
is never inserted when the source is an anonymous rvalue (i.e., a
temporary as featured in the function hun above).
All named temporaries that are stack-allocated inside a function and
then returned elide a call to this(this).
There is no guarantee that other potential elisions are observed.
So, in other cases, the compiler may or may not elide copies, depending on the current compiler implementation and optimization level (e.g. if you pass an lvalue to a function that takes it by value, and that variable is never referenced again after the function call).
So, if you have
void foo(Bar bar)
{}
then whether the argument to foo gets moved or not depends on whether it was an rvalue or an lvalue. If it's an rvalue, it will be moved, whereas if it's an lvalue, it probably won't be (but might depending on the calling code and the compiler).
So, if you have
void foo(UniquePtr!T ptr)
{}
ptr will be moved if foo was passed an rvalue and may or may not be moved it it's passed an lvalue (though generally not). So, what happens with the internals of UniquePtr depends on how you implemented it. If UniquePtr disabled the postblit constructor so that it can't be copied, then passing an rvalue will move the argument, and passing an lvalue will result in a compilation error (since the rvalue is guaranteed to be moved, whereas the lvalue is not).
Now, what you have is
this(UniquePtr!T that)
{
this.ptr = that.ptr;
that.ptr = null;
}
which appears to act like the current type has the same members as those of its argument. So, I assume that what you're actually trying to do here is a copy constructor / move constructor for UniquePtr and not a constructor for an arbitrary type that takes a UniquePtr!T. And if that's what you're doing, then you'd want a postblit constructor - this(this) - and not one that takes the same type as the struct itself (since D does not have copy constructors). So, if what you want is a copy constructor, then you do something like
this(this)
{
// Do any deep copying you want here. e.g.
arr = arr.dup;
}
But if a bitwise copy of your struct's elements works for your type, then you don't need a postblit constructor. But moving is built-in, so you don't need to declare a move constructor regardless (a move will just blit the struct's members). Rather, if what you want is to guarantee that the object is moved and never copied, then what you want to do is disable the struct's postblit constructor. e.g.
#disable this(this);
Then any and all times that you pass a UniquePtr!T anywhere, it's guaranteed to be a move or a compilation error. And while I would have thought that you might have to disable opAssign separately to disable assignment, from the looks of it (based on the code that I just tested), you don't even have to disable assignment separately. Disabling the postblit constructor also disables the assignment operator. But if that weren't the case, then you'd just have to disable opOpAssign as well.
JMD answer covers theoretical part of move semantics, I can extend it with a very simplified example implementation:
struct UniquePtr(T)
{
private T* ptr;
#disable this(this);
UniquePtr release()
{
scope(exit) this.ptr = null;
return UniquePtr(this.ptr);
}
}
// some function that takes argument by value:
void foo ( UniquePtr!int ) { }
auto p = UniquePtr!int(new int);
// won't compile, postblit constructor is disabled
foo(p);
// ok, release() returns a new rvalue which is
// guaranteed to be moved without copying
foo(p.release());
// release also resets previous pointer:
assert(p.ptr is null);
I think I can answer it myself. Quoting the "The Programming Language":
All anonymous rvalues are moved, not copied. A call to this ( this ) is never inserted
when the source is an anonymous rvalue (i.e., a temporary as featured in
the function hun above).
If I understood it correctly, this means that this(Struct that) will never be a copy, because it only accepts rvalues in the first place.

Does Passing STL Containers Make A Copy?

I can't remember whether passing an STL container makes a copy of the container, or just another alias. If I have a couple containers:
std::unordered_map<int,std::string> _hashStuff;
std::vector<char> _characterStuff;
And I want to pass those variables to a function, can I make the function as so:
void SomeClass::someFunction(std::vector<char> characterStuff);
Or would this make a copy of the unordered_map / vector? I'm thinking I might need to use shared_ptr.
void SomeClass::someFunction(std::shared_ptr<std::vector<char>> characterStuff);
It depends. If you are passing an lvalue in input to your function (in practice, if you are passing something that has a name, to which the address-of operator & can be applied) then the copy constructor of your class will be invoked.
void foo(vector<char> v)
{
...
}
int bar()
{
vector<char> myChars = { 'a', 'b', 'c' };
foo(myChars); // myChars gets COPIED
}
If you are passing an rvalue (roughly, something that doesn't have a name and to which the address-of operator & cannot be applied) and the class has a move constructor, then the object will be moved (which is not, beware, the same as creating an "alias", but rather transferring the guts of the object into a new skeleton, making the previous skeleton useless).
In the invocation of foo() below, the result of make_vector() is an rvalue. Therefore, the object it returns is being moved when given in input to foo() (i.e. vector's move constructor will be invoked):
void foo(vector<char> v);
{
...
}
vector<char> make_vector()
{
...
};
int bar()
{
foo(make_vector()); // myChars gets MOVED
}
Some STL classes have a move constructor but do not have a copy constructor, because they inherently are meant to be non-copiable (for instance, unique_ptr). You won't get a copy of a unique_ptr when you pass it to a function.
Even for those classes that do have a copy constructor, you can still force move semantics by using the std::move function to change your argument from an lvalue into an rvalue, but again that doesn't create an alias, it just transfers the ownership of the object to the function you are invoking. This means that you won't be able to do anything else with the original object other than reassigning to it another value or having it destroyed.
For instance:
void foo(vector<char> v)
{
...
}
vector<char> make_vector()
{
...
};
int bar()
{
vector<char> myChars = { 'a', 'b', 'c' };
foo(move(myChars)); // myChars gets MOVED
cout << myChars.size(); // ERROR! object myChars has been moved
myChars = make_vector(); // OK, you can assign another vector to myChars
}
If you find this whole subject of lvalue and rvalue references and move semantics obscure, that's very understandable. I personally found this tutorial quite helpful:
http://thbecker.net/articles/rvalue_references/section_01.html
You should be able to find some info also on http://www.isocpp.org or on YouTube (look for seminars by Scott Meyers).
Yes, it'll copy the vector because you're passing by value. Passing by value always makes a copy or move (which may be elided under certain conditions, but not in your case). If you want to refer to the same vector inside the function as outside, you can just pass it by reference instead. Change your function to:
void SomeClass::someFunction(std::vector<char>& characterStuff);
The type std::vector<char>& is a reference type, "reference to std::vector<char>". The name characterStuff will act as an alias for the object referred to by _characterStuff.
C++ is based on values: When passing object by value you get independent copies. If you don't want to get a copy, you can use a reference or a const reference, instead:
void SomeClass::someFunction(std::vector<char>& changable) { ... }
void SomeClass::otherFunction(std::vector<char> const& immutable) { ... }
When the called function shouldn't be able to change the argument but you don't want to create a copy of the object, you'd want to pass by const&. Normally, I wouldn't use something like a std::shared_ptr<T> instead. There are uses of this type by certainly not to prevent copying when calling a function.

is it possible to restrict class instances to be used only as temporaries?

is it possible to restrict class instances to be used only as rvalues (e.g. temporaries)?
for example, I have class Wrapper whose constructor takes A const& and saves this reference in its member. It's a dangerous because lifetime of Wrapper instance cannot be longer than lifetime of A instance, but it's fine if Wrapper is temporary.
I think that even wanting to do this is a sign of a really bad design.
However, you could make all constructors private and make a friend function that returns an rvalue. That should do the trick.
I don't think it would be safe:
const A &a = YourClass( tmp );
YourClass in this case is the class you're looking for which only allow temporary instances, tmp is the temporary value you pass to the constructor.
It's possible (ie: safe, defined behavior) to have a constant reference to a temporary (ie: a), but the temporary itself (such instance of YourClass) has got a reference to tmp which is no longer valid after that expression is evaluated.
Not exactly the answer you are looking for, but have you thought about weak pointers? (for example, boost::weak_ptr). In this case, the original A would be held in a shared_ptr and the Wrapper constructor accepts a weak_ptr. The neat thing with this approach is that, before each usage of the weak_ptr, you can attempt to lock() which will give you a shared_ptr - if that fails, you know that A is gone and Wrapper cannot function... But it's handled cleanly...
This might do the job unless your class has public data members.
Basically, the idea is not to restrict the construction of the wrapper but to make sure that instances can be used (just like you said) only as long as they are temporary values. One can achieve this by overloading all methods and deleting (or making them private) those that refer to const&.
Here's a simple example:
class Wrapper
{
public:
Wrapper() = default;
Wrapper(const std::string& name) : name(name) {}
void process() && { std::cout << "Greetings from " << name << std::endl; }
// Only temporary instances of this class are allowed!
void process() const & = delete;
private:
std::string name;
};
And some use cases:
Wrapper("John").process(); // intended use case
Wrapper j; // create whatever you want
j.process(); // error C2280: 'void Wrapper::process(void) const &': attempting to reference a deleted function
std::move(j).process(); // this is still possible
const Wrapper& t = Wrapper(); // bind the temporary to a const reference - not a problem because ...
t.process(); // error C2280: 'void Wrapper::process(void) const &': attempting to reference a deleted function
The obvious disadvantages are:
You have to overload every public member function.
The error message is delayed and not very informative.
A similar thing has been done in the standard. The make routines for std::reference_wrapper do not accept temporaries.
Note that they considered another subtlety: the overload uses const T&& instead of T&&. This can be important in our case as well. For example, if your wrapper is deliberately designed to be noncopyable and you use make routines such as
const Wrapper make_wrapper();
instead of
Wrapper make_wrapper();
In this case, you might want to replace
void process() &&;
by
void process() const &&;
I'd not bother enforcing this at compile time, as there are always going to be corner cases where this would be overly restrictive, limiting the usefulness of the class, but rather wrap tools like valgrind or Purify so I can spot places where invalidated references are used.
I believe in C++17 and later you can get approximately what you want by doing the following:
Delete the move constructor for your type (and don't define a copy constructor).
Always accept your type by value in APIs.
So, for example:
#include <type_traits>
#include <utility>
// A non-moveable, non-copyable type.
struct CantMove {
CantMove(CantMove&&) = delete;
CantMove(int) {} // Some other constructor
};
static_assert(!std::is_move_constructible_v<CantMove>);
static_assert(!std::is_copy_constructible_v<CantMove>);
// A function that accepts it by value.
bool AcceptByValue(CantMove input) { return true; }
// It's possible to call the value-accepting API when the input is a prvalue
// (which in previous versions of C++ would have been a temporary).
bool unused = AcceptByValue(CantMove(0));
// But it's not possible to call with a named value, even when casted to an
// rvalue reference. This doesn't compile.
CantMove cant_move(0);
bool unused_2 = AcceptByValue(std::move(cant_move));
It's possible to provide the value-accepting function with what we previously called a temporary because guaranteed copy elision says that there isn't even a temporary involved anymore—the only CantMove object created is the function parameter itself, so there is no move- or copy-construction involved. In contrast it's not possible to call with std::move(cant_move) because that would involve move-constructing the function parameter, and the type is not move-constructible.
Of course it's still possible to initialize a CantMove directly:
CantMove foo{0};
But if you own all of the APIs that accept a CantMove and make them all accept by value, then you can't actually do anything with foo afterward. This means it would be hard for a user to do this by mistake and not realize the problem.
Yes, you could.
You would make the constructor and regular copy-constructor/assign private but make the r-value move semantics (C++0x) public.
You would have a static or friend constructor to create the temporary.
In 2003 C++ you would also be able to use this to bind to a const reference.
Of course you'd have the issue that your const reference would probably become invalidated after the statement.