The Guidelines Support Library introduced not_null<T> who's purpose is to enforce an invariant on pointer-like types, advertently on smart pointers. However it's a known issue that not_null<unique_ptr<T>> doesn't work.
As far as I see the reason is that unique_ptr<T> is not copy-constructible and not_null<T> doesn't have a constructor that would move from its T. not_null<T> is not default-constructible either because it would break it's invariant. Even if we could construct not_null<unique_ptr<T>>, it would be impossible to meaningfully reach the unique_ptr inside because we couldn't copy unique_ptr and moving it would leave not_null<T> with a nullptr. It looks like a perfect trap.
I argued that we could legally move from a not_null<T> object in a specific context: just before it goes out of scope. In other words, moving from it should be the last access before destruction. That way the object with broken invariant wouldn't be observable to the rest of the program. (It would be observable for not_null's own code only.)
In the following examples let's assume that we can move from not_null<T>.
not_null<unique_ptr<int>> f()
{
return make_unique<int>(1);
}
void g(not_null<unique_ptr<int>> p)
{
...
}
void h()
{
auto p = f();
g(make_unique<int>(2));
}
Is my assumption correct that state of the not_null<unique_ptr<int>> returned from f() couldn't leak after moving from it (just for the example)?
Is my assumption correct that state of the not_null<unique_ptr<int>> passed to g() couldn't leak after moving from it (just for the example)?
Is it possible to allow this special kind of moving while prohibiting the common case of moving in C++14/17?
1&2: Ignoring the fact that elision would render the question moot on any compiler worth using, yes. Also ignoring the fact that unique_ptr cannot "leak".
3: No.
This has been the subject of some debate on the ISO C++ proposals mailing list. The general concept is that of a "destructive move", where the act of moving from an object and destroying it are performed in the same call. But this would have to be a language feature; there is no way in C++14 to tell whether a move constructor/assignment is being called such that the given object is certainly about to be destroyed.
Related
Have found comparable questions but not exactly with such a case.
Take the following code for example:
#include <iostream>
#include <string>
#include <vector>
struct Inner
{
int a, b;
};
struct Outer
{
Inner inner;
};
std::vector<Inner> vec;
int main()
{
Outer * an_outer = new Outer;
vec.push_back(std::move(an_outer->inner));
delete an_outer;
}
Is this safe? Even if those were polymorphic classes or ones with custom destructors?
My concern regards the instance of "Outer" which has a member variable "inner" moved away. From what I learned, moved things should not be touched anymore. However does that include the delete call that is applied to outer and would technically call delete on inner as well (and thus "touch" it)?
Neither std::move, nor move semantics more generally, have any effect on the object model. They don't stop objects from existing, nor prevent you from using those objects in the future.
What they do is ask to borrow encapsulated resources from the thing you're "moving from". For example, a vector, which directly only stores a pointer some dynamically-allocated data: the concept of ownership of that data can be "stolen" by simply copying that pointer then telling the vector to null the pointer and never have anything to do with that data again. It's yielded. The data belongs to you now. You have the last pointer to it that exists in the universe.
All of this is achieved simply by a bunch of hacks. The first is std::move, which just casts your vector expression to vector&&, so when you pass the result of it to a construction or assignment operation, the version that takes vector&& (the move constructor, or move-assignment operator) is triggered instead of the one that takes const vector&, and that version performs the steps necessary to do what I described in the previous paragraph.
(For other types that we make, we traditionally keep following that pattern, because that's how we can have nice things and persuade people to use our libraries.)
But then you can still use the vector! You can "touch" it. What exactly you can do with it is discoverable from the documentation for vector, and this extends to any other moveable type: the constraints emplaced on your usage of a moved-from object depend entirely on its type, and on the decisions made by the person who designed that type.
None of this has any impact on the lifetime of the vector. It still exists, it still takes memory, and it will still be destructed when the time comes. (In this particular example you can actually .clear() it and start again adding data to a new buffer.)
So, even if ints had any sort of concept of this (they don't; they encapsulate no indirectly-stored data, and own no resources; they have no constructors, so they also have no constructors taking int&&), the delete "touch"ing them would be entirely safe. And, more generally, none of this depends on whether the thing you've moved from is a member or not.
More generally, if you had a type T, and an object of that type, and you moved from it, and one of the constraints for T was that you couldn't delete it after moving from it, that would be a bug in T. That would be a serious mistake by the author of T. Your objects all need to be destructible. The mistake could manifest as a compilation failure or, more likely, undefined behaviour, depending on what exactly the bug was.
tl;dr: Yes, this is safe, for several reasons.
std::move is a cast to an rvalue-reference, which primarily changes which constructor/assignment operator overload is chosen. In your example the move-constructor is the default generated move-constructor, which just copies the ints over so nothing happens.
Whether or not this generally safe depends on the way your classes implement move construction/assignment. Assume for example that your class instead held a pointer. You would have to set that to nullptr in the moved-from class to avoid destroying the pointed-to data, if the moved-from class is destroyed.
Because just defining move-semantics is a custom way almost always leads to problems, the rule of five says that if you customize any of:
the copy constructor
the copy assignment operator
the move constructor
the move assignment operator
the destructor
you should usually customize all to ensure that they behave consistently with the expectations a caller would usually have for your class.
I've been trying to wrap my head around how move semantics in C++11 are supposed to work, and I'm having a good deal of trouble understanding what conditions a moved-from object needs to satisfy. Looking at the answer here doesn't really resolve my question, because can't see how to apply it to pimpl objects in a sensible way, despite arguments that move semantics are perfect for pimpls.
The easiest illustration of my problem involves the pimpl idiom, like so:
class Foo {
std::unique_ptr<FooImpl> impl_;
public:
// Inlining FooImpl's constructors for brevity's sake; otherwise it
// defeats the point.
Foo() : impl_(new FooImpl()) {}
Foo(const Foo & rhs) : impl_(new FooImpl(*rhs.impl_)) {}
Foo(Foo && rhs) : impl_(std::move(rhs.impl_)) {}
Foo & operator=(Foo rhs)
{
std::swap(impl_, rhs.impl_);
return *this;
}
void do_stuff ()
{
impl_->do_stuff;
}
};
Now, what can I do once I've moved from a Foo? I can destroy the moved-from object safely, and I can assign to it, both of which are absolutely crucial. However, if I try to do_stuff with my Foo, it will explode. Before I added move semantics for my definition of Foo, every Foo satisfied the invariant that it could do_stuff, and that's no longer the case. There don't seem to be many great alternatives, either, since (for example) putting the moved-from Foo would involve a new dynamic allocation, which partially defeats the purpose of move semantics. I could check whether impl_ in do_stuff and initialize it to a default FooImpl if it is, but that adds a (usually spurious) check, and if I have a lot of methods it would mean remembering to do the check in every one.
Should I just give up on the idea that being able to do_stuff is a reasonable invariant?
You define and document for your types what a 'valid' state is and what operation can be performed on moved-from objects of your types.
Moving an object of a standard library type puts the object into an unspecified state, which can be queried as normal to determine valid operations.
17.6.5.15 Moved-from state of library types
[lib.types.movedfrom]
Objects of types defined in the C++ standard library may be moved from
(12.8). Move operations may be explicitly specified or implicitly
generated. Unless otherwise specified, such moved-from objects shall
be placed in a valid but unspecified state.
The object being in a 'valid' state means that all the requirements the standard specifies for the type still hold true. That means you can use any operation on a moved-from, standard library type for which the preconditions hold true.
Normally the state of an object is known so you don't have to check if it meets the preconditions for each operation you want to perform. The only difference with moved-from objects is that you don't know the state, so you do have to check. For example, you should not pop_back() on a moved-from string until you have queried the state of the string to determine that the preconditions of pop_back() are met.
std::string s = "foo";
std::string t(std::move(s));
if (!s.empty()) // empty has no preconditions, so it's safe to call on moved-from objects
s.pop_back(); // after verifying that the preconditions are met, pop_back is safe to call on moved-from objects
The state is probably unspecified because it would be onerous to create a single useful set of requirements for all different implementations of the standard library.
Since you are responsible not only for the specification but also the implementation of your types, you can simply specify the state and obviate the need for querying. For example it would be perfectly reasonable to specify that moving from your pimpl type object causes do_stuff to become an invalid operation with undefined behavior (via dereferencing a null pointer). The language is designed such that moving only occurs either when it's not possible to do anything to the moved-from object, or when the user has very obviously and very explicitly indicated a move operation, so a user should never be surprised by a moved-from object.
Also note that the 'concepts' defined by the standard library do not make any allowances for moved-from objects. That means that in order to meet the requirements for any of the concepts defined by the standard library, moved-from objects of your types must still fulfill the concept requirements. This means that if objects of your type don't remain in a valid state (as defined by the relevant concept) then you cannot use it with the standard library (or the result is undefined behavior).
However, if I try to do_stuff with my Foo, it will explode.
Yes. So will this:
vector<int> first = {3, 5, 6};
vector<int> second = std::move(first);
first.size(); //Value returned is undefined. May be 0, may not
The rule the standard uses is to leave the object in a valid (meaning that the object works) but unspecified state. This means that the only functions you can call are those that have no conditions on the current state of the object. For vector, you can use its copy/move assignment operators, as well as clear and empty, and several other operations. So you can do this:
vector<int> first = {3, 5, 6};
vector<int> second = std::move(first);
first.clear(); //Cause the vector to become empty.
first.size(); //Now the value is guaranteed to be 0.
For your case, copy/move assignment (from either side) should still work, as should the destructor. But all other functions of yours have a precondition based on the state of not having been moved from.
So I don't see your problem.
If you wanted to ensure that no instance of a Pimpl'd class could be empty, then you would implement proper copy semantics and forbid moving. Movement requires the possibility of an object being in an empty state.
I've been trying to wrap my head around how move semantics in C++11 are supposed to work, and I'm having a good deal of trouble understanding what conditions a moved-from object needs to satisfy. Looking at the answer here doesn't really resolve my question, because can't see how to apply it to pimpl objects in a sensible way, despite arguments that move semantics are perfect for pimpls.
The easiest illustration of my problem involves the pimpl idiom, like so:
class Foo {
std::unique_ptr<FooImpl> impl_;
public:
// Inlining FooImpl's constructors for brevity's sake; otherwise it
// defeats the point.
Foo() : impl_(new FooImpl()) {}
Foo(const Foo & rhs) : impl_(new FooImpl(*rhs.impl_)) {}
Foo(Foo && rhs) : impl_(std::move(rhs.impl_)) {}
Foo & operator=(Foo rhs)
{
std::swap(impl_, rhs.impl_);
return *this;
}
void do_stuff ()
{
impl_->do_stuff;
}
};
Now, what can I do once I've moved from a Foo? I can destroy the moved-from object safely, and I can assign to it, both of which are absolutely crucial. However, if I try to do_stuff with my Foo, it will explode. Before I added move semantics for my definition of Foo, every Foo satisfied the invariant that it could do_stuff, and that's no longer the case. There don't seem to be many great alternatives, either, since (for example) putting the moved-from Foo would involve a new dynamic allocation, which partially defeats the purpose of move semantics. I could check whether impl_ in do_stuff and initialize it to a default FooImpl if it is, but that adds a (usually spurious) check, and if I have a lot of methods it would mean remembering to do the check in every one.
Should I just give up on the idea that being able to do_stuff is a reasonable invariant?
You define and document for your types what a 'valid' state is and what operation can be performed on moved-from objects of your types.
Moving an object of a standard library type puts the object into an unspecified state, which can be queried as normal to determine valid operations.
17.6.5.15 Moved-from state of library types
[lib.types.movedfrom]
Objects of types defined in the C++ standard library may be moved from
(12.8). Move operations may be explicitly specified or implicitly
generated. Unless otherwise specified, such moved-from objects shall
be placed in a valid but unspecified state.
The object being in a 'valid' state means that all the requirements the standard specifies for the type still hold true. That means you can use any operation on a moved-from, standard library type for which the preconditions hold true.
Normally the state of an object is known so you don't have to check if it meets the preconditions for each operation you want to perform. The only difference with moved-from objects is that you don't know the state, so you do have to check. For example, you should not pop_back() on a moved-from string until you have queried the state of the string to determine that the preconditions of pop_back() are met.
std::string s = "foo";
std::string t(std::move(s));
if (!s.empty()) // empty has no preconditions, so it's safe to call on moved-from objects
s.pop_back(); // after verifying that the preconditions are met, pop_back is safe to call on moved-from objects
The state is probably unspecified because it would be onerous to create a single useful set of requirements for all different implementations of the standard library.
Since you are responsible not only for the specification but also the implementation of your types, you can simply specify the state and obviate the need for querying. For example it would be perfectly reasonable to specify that moving from your pimpl type object causes do_stuff to become an invalid operation with undefined behavior (via dereferencing a null pointer). The language is designed such that moving only occurs either when it's not possible to do anything to the moved-from object, or when the user has very obviously and very explicitly indicated a move operation, so a user should never be surprised by a moved-from object.
Also note that the 'concepts' defined by the standard library do not make any allowances for moved-from objects. That means that in order to meet the requirements for any of the concepts defined by the standard library, moved-from objects of your types must still fulfill the concept requirements. This means that if objects of your type don't remain in a valid state (as defined by the relevant concept) then you cannot use it with the standard library (or the result is undefined behavior).
However, if I try to do_stuff with my Foo, it will explode.
Yes. So will this:
vector<int> first = {3, 5, 6};
vector<int> second = std::move(first);
first.size(); //Value returned is undefined. May be 0, may not
The rule the standard uses is to leave the object in a valid (meaning that the object works) but unspecified state. This means that the only functions you can call are those that have no conditions on the current state of the object. For vector, you can use its copy/move assignment operators, as well as clear and empty, and several other operations. So you can do this:
vector<int> first = {3, 5, 6};
vector<int> second = std::move(first);
first.clear(); //Cause the vector to become empty.
first.size(); //Now the value is guaranteed to be 0.
For your case, copy/move assignment (from either side) should still work, as should the destructor. But all other functions of yours have a precondition based on the state of not having been moved from.
So I don't see your problem.
If you wanted to ensure that no instance of a Pimpl'd class could be empty, then you would implement proper copy semantics and forbid moving. Movement requires the possibility of an object being in an empty state.
I understand that, without R-value references, perfect forwarding in C++ would be impossible.
However, I would like to know: is there anything else that necessitates them?
For example, this page points out this example in which R-value references are apparently necessary:
X foo();
X x;
// perhaps use x in various ways
x = foo();
The last line above:
Destructs the resource held by x,
Clones the resource from the temporary returned by foo,
Destructs the temporary and thereby releases its resource.
However, it seems to me that a simple change would have fixed the problem, if swap were implemented properly:
X foo();
X x;
// perhaps use x in various ways
{
X y = foo();
swap(x, y);
}
So it seems to me that r-value references not necessary for this optimization. (Is that correct?)
So, what are some problems that could not be solved with r-value references (except for perfect forwarding, about which I already know)?
So, what are some problems that could not be solved with r-value references (except for perfect forwarding, about which I already know)?
Yes. In order for the swap trick to work (or at least, work optimally), the class must be designed to be in an empty state when constructed. Imagine a vector implementation that always reserved a few elements, rather than starting off totally empty. Swapping from such a default-constructed vector with an already existing vector would mean doing an extra allocation (in the default constructor of this vector implementation).
With an actual move, the default-constructed object can have allocated data, but the moved-from object can be left in an unallocated state.
Also, consider this code:
std::unique_ptr<T> make_unique(...) {return std::unique_ptr<T>(new T(...));}
std::unique_ptr<T> unique = make_unique();
This is not possible without language-level move semantics. Why? Because of the second statement. By the standard, this is copy initialization. That, as the name suggests, requires the type to be copyable. And the whole point of unique_ptr is that it is, well, unique. IE: not copyable.
If move semantics didn't exist, you couldn't return it. Oh yes, you can talk about copy elision and such, but remember: elision is an optimization. A conforming compiler must still detect that the copy constructor exists and is callable; the compiler simply has the option to not call it.
So no, you can't just emulate move semantics with swap. And quite frankly, even if you could, why would you ever want to?
You have to write swap implementations yourself. So if you've got a class with 6 members, your swap function has to swap each member in turn. Recursively through all base classes and such too.
C++11 (though not VC10 or VC2012) allows the compiler to write the move constructors for you.
Yes, there are circumstances where you can get away without it. But they look incredibly hacky, are hard to see why you're doing things that way, and most important of all, make things difficult for both the reader and the writer.
When people want to do something a lot for performance sake that makes their code tedious to read, difficult to write, and error-prone to maintain (add another member to the class without adding it to the swap function, for example), then you're looking at something that probably should become part of the language. Especially when it's something that the compiler can handle quite easily.
In an answer to a question about std::stack::pop() I claimed that the reason pop does not return the value is for exception safety reason (what happens if the copy constructor throws).
#Konrad commented that now with move semantics this is no longer relevant. Is this true?
AFAIK, move constructors can throw, but perhaps with noexcept it can still be achieved.
For bonus points what thread safety guarantees can this operation supply?
Of course, not every type is move-enabled and C++0x even allows throwing move constructors. As long as constructing the object from an rvalue may throw it cannot be exception-safe. However, move semantics allows you to have many types that are nothrow-constructible given an rvalue source.
Conditional support for this could be done with SFINAE. But even without such a conditional member function nothing stops you from writing:
auto stack = ...;
auto elem = std::move_if_noexcept(stack.back());
stack.pop_back();
which is makes the strong exception guarantee even in case your move constructor doesn't give the strong guarantee.
As for the bonus question, that would offer no thread-safety. Consider that, as an example, most implementations of std::vector have three data elements (pointer to beginning of memory, pointer one beyond end of used data, pointer one beyond end of allocated memory). Move semantics allow you to move the contents of the vector without the need to reallocate and copy values, but that has nothing to do with thread safety. You would have to use thread-safe constructs to make the structure thread safe (as moving does not imply by any means atomic)