Perfect forwarding and constructors - c++

I am trying to understand the interaction of perfect forwarding and constructors. My example is the following:
#include <utility>
#include <iostream>
template<typename A, typename B>
using disable_if_same_or_derived =
std::enable_if_t<
!std::is_base_of<
A,
std::remove_reference_t<B>
>::value
>;
template<class T>
class wrapper {
public:
// perfect forwarding ctor in order not to copy or move if unnecessary
template<
class T0,
class = disable_if_same_or_derived<wrapper,T0> // do not use this instead of the copy ctor
> explicit
wrapper(T0&& x)
: x(std::forward<T0>(x))
{}
private:
T x;
};
class trace {
public:
trace() {}
trace(const trace&) { std::cout << "copy ctor\n"; }
trace& operator=(const trace&) { std::cout << "copy assign\n"; return *this; }
trace(trace&&) { std::cout << "move ctor\n"; }
trace& operator=(trace&&) { std::cout << "move assign\n"; return *this; }
};
int main() {
trace t1;
wrapper<trace> w_1 {t1}; // prints "copy ctor": OK
trace t2;
wrapper<trace> w_2 {std::move(t2)}; // prints "move ctor": OK
wrapper<trace> w_3 {trace()}; // prints "move ctor": why?
}
I want my wrapper to have no overhead at all. In particular, when marshalling a temporary into the wrapper, as with w_3, I would expect the trace object to be created directly in place, without having to call the move ctor. However, there is a move ctor call, which makes me think a temporary is created and then moved from. Why is the move ctor called? How not to call it?

I would expect the trace object to be created directly in place,
without having to call the move ctor.
I don't know why you expect that. Forwarding does exactly this: moves or copies 1). In your example you create a temporary with trace() and then the forwarding moves it into x
If you want to construct a T object in place then you need to pass the arguments to the construction of T, not an T object to be moved or copied.
Create an in place constructor:
template <class... Args>
wrapper(std::in_place_t, Args&&... args)
:x{std::forward<Args>(args)...}
{}
And then call it like this:
wrapper<trace> w_3 {std::in_place};
// or if you need to construct an `trace` object with arguments;
wrapper<trace> w_3 {std::in_place, a1, a2, a3};
Addressing a comment from the OP on another answer:
#bolov Lets forget perfect forwarding for a minute. I think the
problem is that I want an object to be constructed at its final
destination. Now if it is not in the constructor, it is now garanteed
to happen with the garanteed copy/move elision (here move and copy are
almost the same). What I don't understand is why this would not be
possible in the constructor. My test case proves it does not happen
according to the current standard, but I don't think this should be
impossible to specify by the standard and implement by compilers. What
do I miss that is so special about the ctor?
There is absolutely nothing special about a ctor in this regard. You can see the exact same behavior with a simple free function:
template <class T>
auto simple_function(T&& a)
{
X x = std::forward<T>(a);
// ^ guaranteed copy or move (depending on what kind of argument is provided
}
auto test()
{
simple_function(X{});
}
The above example is similar with your OP. You can see simple_function as analog to your wrapper constructor and my local x variable as analog to your data member in wrapper. The mechanism is the same in this regard.
In order to understand why you can't construct the object directly in the local scope of simple_function (or as data member in your wrapper object in your case) you need to understand how guaranteed copy elision works in C++17 I recommend this excelent answer.
To sum up that answer: basically a prvalue expression doesn't materializez an object, instead it is something that can initialize an object. You hold on to the expression for as long as possible before you use it to initialize an object (thus avoiding some copy/moves). Refer to the linked answer for a more in-depth yet friendly explanation.
The moment your expression is used to initialize the parameter of simple_foo (or the parameter of your constructor) you are forced to materialize an object and lose your expression. From now on you don't have the original prvalue expression anymore, you have a created materialized object. And this object now needs to be moved into your final destination - my local x (or your data member x).
If we modify my example a bit we can see guaranteed copy elision at work:
auto simple_function(X a)
{
X x = a;
X x2 = std::move(a);
}
auto test()
{
simple_function(X{});
}
Without elision things would go like this:
X{} creates a temporary object as argument for simple_function. Lets call it Temp1
Temp1 is now moved (because it is a prvalue) into the parameter a of simple_function
a is copied (because a is an lvalue) into x
a is moved (because std::move casts a to an xvalue) to x2
Now with C++17 guaranteed copy elision
X{} no longer materializez an object on the spot. Instead the expression is held onto.
the parameter a of simple_function can now by initialized from the X{} expression. No copy or move involved nor required.
The rest is now the same:
a is copied into x1
a is moved into x2
What you need to understand: once you have named something, that something must exist. The surprisingly simple reason for that is that once you have a name for something you can reference it multiple times. See my answer on this other question. You have named the parameter of wrapper::wrapper. I have named the parameter of simple_function. That is the moment you lose your prvalue expression to initialize that named object.
If you want to use the C++17 guaranteed copy elision and you don't like the in-place method you need to avoid naming things :) You can do that with a lambda. The idiom I see most often, including in the standard, is the in-place way. Since I haven't seen the lambda way in the wild, I don't know if I would recommend it. Here it is anyway:
template<class T> class wrapper {
public:
template <class F>
wrapper(F initializer)
: x{initializer()}
{}
private:
T x;
};
auto test()
{
wrapper<X> w = [] { return X{};};
}
In C++17 this grantees no copies and/or moves and that it works even if X has deleted copy constructors and move constructors. The object will be constructed at it's final destination, just like you want.
1) I am talking about the forwarding idiom, when used properly. std::forward is just a cast.

A reference (either lvalue reference or rvalue reference) must be bound to an object, so when the reference parameter x is initialized, a temporary object is required to be materialized anyway. In this sense, perfect forwarding is not that "perfect".
Technically, to elide this move, the compiler must know both the initializer argument and the definition of the constructor. This is impossible because they may lie in different translation units.

Related

Copy constructor called endlessly from lambda capture group

I wrote the following class to create values of any type which are either fixed or recalculated everytime the call operator is used on them:
template <typename T>
class DynamicValue {
private:
std::variant<T, std::function<T()>> getter;
public:
DynamicValue(const T& constant) : getter(constant){};
template <typename F, typename = std::enable_if_t<std::is_invocable_v<F>>>
DynamicValue(F&& function) : getter(function) {}
DynamicValue(const T* pointer) : DynamicValue([pointer]() { return *pointer; }) {}
DynamicValue(const DynamicValue& value) : getter(value.getter) {}
DynamicValue(DynamicValue& value) : DynamicValue((const DynamicValue&) value) {}
~DynamicValue() {}
T operator()() const { return getter.index() == 0 ? std::get<T>(getter) : std::get<std::function<T()>>(getter)(); }
};
I also wrote this function, which takes a DynamicValue<int> and returns another DynamicValue<int> which returns its value plus 1:
DynamicValue<int> plus1(DynamicValue<int> a) {
return [a] { return a() + 1; };
}
However, when I attempt to do use it, the program crashes:
DynamicValue<int> a = 1;
DynamicValue<int> b = plus1(a);
You can try a live example here.
After some testing, I think the problem lies in the copy constructor, which is being called endlessly, but I'm not sure how to fix it. How can I avoid this behavior?
Some important pieces of this code:
A lambda captures a DynamicValue object by value (copy).
The lambda is used to initialize a std::variant as a std::function alternative.
There is no explicit move constructor for DynamicValue, so the template for invocable objects is used as the move constructor.
The problematic code path starts with the request to construct a DynamicValue object from the lambda. This invokes the template constructor, which attempts to copy the lambda into the std::function alternative of the variant. So far, so good. Copying (not moving) the lambda copies the captured object without problems.
However, this procedure works when the CopyConstructible named requirement is satisfied. Part of this named requirement is being MoveConstructible. In order for a lambda to satisfy MoveConstructible, all of its captures have to satisfy that named requirement. Is this the case for DynamicValue? What happens when your standard library tries to move the lambda (hence also the captured object), with copying as the fallback? While DynamicValue has no explicit move constructor, it is invocable...
When F is DynamicValue<T>, the template constructor serves as the move constructor. It tries to initialize the variant by converting the source DynamicValue (the captured copy of a in the question's code) into a std::function. This is allowed, a copy of the source is made, and the process continues until the copy needs to be moved, at which point the move constructor is again invoked. This time, it tries to initialize the variant by converting the copy of the source DynamicValue into a std::function. This is allowed, a copy of the copy of the source is made, and the process continues until the copy of the copy needs to be moved, at which point the move constructor is again invoked. Etc.
Instead of moving the DynamicValue into the new object, each "move constructor" tries to move the DynamicValue into the variant of the new object. This would add another layer of overhead with each move, except the recursive calls blow up before construction finishes.
The solution is to make DynamicValue move constructible. There are at least two ways to do this.
1) Explicitly provide a move constructor.
DynamicValue(DynamicValue&& value) : getter(std::move(value.getter)) {}
2) Exclude DynamicValue from being a template argument to the template constructor.
template <typename F, typename = std::enable_if_t<std::is_invocable_v<F>>,
typename = std::enable_if_t<!std::is_same_v<std::decay_t<F>, DynamicValue>>>
DynamicValue(F&& function) : getter(function) {}
Note that this excludes only DynamicValue<T> from being a template argument, not DynamicValue<U> when U is not T. That might be another issue to contemplate.
You might want to see if this also fixes whatever problem led you to define a second copy constructor. That may have been a band-aid approach that did not address this underlying issue.

How to know or test if a given type is going to be moved

I'm not looking for a type trait for movable types, nor rules for automatic generation of move operations. What I'm looking for is a general guide to know if a given type is going to be moved or copied, or a way to figure it myself with testing.
In some cases, the moving operation is performed without the user ever noticing, for example:
void f(std::string) { ... }
void f_c(const std::string) { ... }
void g()
{
f(std::string("Hello world!")); // moved, isn't it?
f("Hello world!"); // moved, isn't it?
f_c(std::string("Hello world!")); // moved, isn't it?
f_c("Hello world!"); // moved, isn't it?
}
Prior to C++11 the code above would incurr in std::string copy from a temporary value to the one passed to f and f_c, from C++11 onwards the std::basic_string provides a move constructor (see here (8)) and the temporary created is moved into the parameter passed to f and f_c.
Sometimes the users were trying to force the move semantics with the std::move function:
std::string return_by_value_1()
{
std::string result("result);
return std::move(result); // Is this moved or not?
}
std::string return_by_value_2()
{
return std::move(std::string("result)); // Is this moved or not?
}
But std::move doesn't move anything1: it only converts lvalues into rvalues references, if the target type doesn't implement move semantics: no move operation is performed... and AFAIK std::moveing in the return_by_value_x functions above prevents the compiler from doing RVO (we're worsening the code!).
So, after the (maybe unnecessary) introduction, I'll ask my questions:
How to know or test if a given type is going to be moved or copied?
This question is about basic types and complex ones:
int f_int(int) { ... };
template <typename F, typename S> void f_pair(std::pair<F, S>) { ... };
struct weird
{
int i;
float f;
std::vector<double> vd;
using complexmap = std::map<std::pair<std::string, std::uint64_t>, std::pair<std::uint32_t, std::uint32_t>>;
complexmap cm;
};
struct silly
{
std::vector<std::pair<const std::string, weird::complexmap>> vcm;
};
f_weird(weird) { ... };
f_silly(silly) { ... };
A basic type can be moved? there's some calls to f_int which implies a move operation?
f_int(1); // this moves or construct an int in-place?
f_int(1 + 2); // this moves or construct an int in-place?
f_int(f_int(1) + 2); // this moves or construct an int in-place?
A complex type with const members can't be moved, isn't it?
f_pair<std::pair<const std::string, int>>({"1", 2}); // unmovable?
f_pair<std::pair<std::string, std::string>>({"1", "2"}); // this moves?
f_silly({{{}, {}}}); // this moves?
The struct weird is moveable?
f_weird({1, .0f, {0.d, 1.d}, {{{"a", 0ull}, {1u, 2u}}}}) // this moves?
How to test by myself the above cases in order to determine if a given type is being moved in a given context?
1Wouldn't be better if it was called std::rvalref or something similar?
How to test by myself the above cases in order to determine if a given
type is being moved in a given context?
You can test whether a derived class would define a defaulted move constructor as deleted via SFINAE. This idea does not work for final classes. A rough sketch that seems to work is
namespace detail {
// No copy constructor. Move constructor not deleted if T has an applicable one.
template <typename T>
struct Test : T { Test(Test&&) = default; };
}
// TODO: Implement unions
template <typename T>
using is_moveable = std::is_move_constructible<
std::conditional_t<std::is_class<T>{}, detail::Test<T>, T>>;
Demo.
The move constructor of Test is a defaulted move constructor according to [dcl.fct.def.default]/5. The relevant quote that makes the above work is then in [class.copy]/11:
A defaulted [..] move constructor for a class X is defined as
deleted (8.4.3) if X has:
a potentially constructed subobject type M (or array thereof) that cannot be [..] moved because overload resolution (13.3), as
applied to M’s corresponding constructor, results in an ambiguity or
a function that is deleted or inaccessible from the defaulted
constructor, [..]
A defaulted move constructor that is defined as deleted is ignored by overload resolution (13.3, 13.4).
For the initialization
Test(Test())
to be valid, as the copy ctor is not implicitly declared, a move constructor must be available. However, according to the above quote, Test's move constructor will be defined as deleted (and thus ignored by overload resolution) if the base class, our given T, does not have an invokeable move constructor. (I am slightly unsure about the case where T does not have any move constructor, I'd be grateful for clarification on the standard there.)

Passing a movable object

Reading an answer in SO, he passed a vector with move. I thought the correct way is to pass it simple without using move:
class B
{
std::vector<T> data;
public:
B(std::vector<T> p) : data(std::move(p)) {}
^^^^^^^^^^^^
?
};
The second way is:
class B
{
std::vector<T> data;
public:
B(std::vector<T> p) : data(p) {}
};
Which one is correct?
The function argument is already taken by value, so a copy will already have been made. The local object p is unquestioningly yours and yours alone, so you can move from it unconditionally.
The beauty of taking the argument by value is that it works for both lvalues and rvalues: For lvalues you make a genuine copy, since there's nothing else you can do, but for rvalues the function argument itself can be constructed by moving, so there's only one expensive construction happening ever, and everything else is moved.
When you construct an object from an lvalue, it will be copied. When you construct an object from a non-const rvalue it can be moved (whether it will be moved depends on the class having a move constructor). In the context of your constructor, p is clearly a lvalue: it has a name. However, it is a local variable and about to go away, i.e., it is save to move from it: std::move(p) makes the object appear as if it is an rvalue: in this context, std::move()ing the value is the right way to go.
Note that the recommendations are different if you return an object: a local value returned in a return statement is automatically moved from. Using std::move() would just reinforce the statement but would also inhibit the possibility of eliding the copy/move entirely. In your constructor the copy/move cannot be elided because copy elision only works with temporary objects or with local objects in return statements.
T f() {
T local;
return local; // good: elided, moved, or copied in this order of preference
}
T g() {
T local;
return std::move(local); // bad: can't be elided and will be moved or copied
}

What is the behaviour of compiler generated move constructor?

Does std::is_move_constructible<T>::value == true imply that T has a usable move constructor?
If so, what is the default behaviour of it?
Consider the following case:
struct foo {
int* ptr;
};
int main() {
{
std::cout << std::is_move_constructible<foo>::value << '\n';
foo f;
f.ptr = (int*)12;
foo f2(std::move(f));
std::cout << f.ptr << ' ' << f2.ptr << '\n';
}
return 0;
}
and the output is:
1
0000000C 0000000C
I thought that f.ptr should be nullptr.
So in this case,
Is f2 move constructed ?
If so, shouldn't the rvalue be invalidated?
How can I know if instances of a class can be properly move-constructed (invalidate the old one)?
(I'm using VS11.)
Update
The default behaviour of move constructor is same as a copy constructor, is it correct?
If it's true,
We always expect a move ctor to steal the resources of the moved-from object, while the default one does not behave as expected, so what's the point of having a default move ctor?
How can I know if a class has a custom move constructor (which can be guaranteed to behave properly)?
It seems that foo f2(std::move(f)); calls the copy ctor when I declared one, see:
struct foo {
int* ptr;
foo() {}
foo(const foo& other) {
std::cout << "copy constructed\n";
}
};
int main() {
{
std::cout << std::is_move_constructible<foo>::value << '\n';
foo f;
foo f2(std::move(f));
}
system("pause");
return 0;
}
Now the output is:
1
copy constructed
If foo has a move constructor, then wouldn't foo f2(std::move(f)) call it?
So now my questions is:
How to know if a class has a move ctor, and if it has one, how can I explicitly call it?
What I'm trying to do is…
template<typename T, bool has_move_ctor>
struct MoveAux;
template<typename T>
struct MoveAux<T, true> {
static void doMove(T* dest, T* src) {
new(dest) T(std::move(*src)); //move ctor
}
};
template<typename T>
struct MoveAux<T, false> {
static void doMove(T* dest, T* src) {
new(dest) T(*src); //copy ctor
src->~T();
}
};
template<typename T>
inline doMove(T* dest, T* src) {
MoveAux<T,/*a trait*/>::doMove(dest, src);
}
So I thought std::is_move_constructible<T>::value can be passed to the template, while now I see that this trait only cares if T t(T()) is a valid expression, it may call T::T(const T&).
Now assume that T is a custom class, then I want the above templates to behave like:
If I don't declare a move ctor, I want that template method calls the MoveAux<T,false>::doMove.
If I declared one, I need it calls to MoveAux<T,true>::doMove.
Is it possible to make this work?
does std::is_move_constructible<T>::value == true implies that T has a usable move constructor?
Either a move constructor or a copy constructor. Remember that the operation of copy construction satisfies all the requirements that are placed upon the operation move construction, and some more.
In Standard terms, a MoveConstructible object is one for which the evaluation of the expression:
T u = rv;
makes u equivalent to the value of rv before the construction; the state of rv after being moved-from is unspecified. But since it is unspecified, this means the state could even be identical to the one rv had before being moved from: In other words, u could be a copy of rv.
In fact, the Standard defines the CopyConstructible concept to be a refinement of the MoveConstructible concept (so everything which is CopyConstructible is also MoveConstructible, but not vice versa).
if so, what is the default behaviour of it?
The behavior of an implicitly generated move constructor is to perform a member-wise move of the data members of the type for which it is generated.
Per Parahgraph 12.8/15 of the C++11 Standard:
The implicitly-defined copy/move constructor for a non-union class X performs a memberwise copy/move
of its bases and members. [ Note: brace-or-equal-initializers of non-static data members are ignored. See
also the example in 12.6.2. —end note ]
Moreover:
1 - is f2 move constructed ?
Yes.
2 - if so, shouldn't the rvalue be invalidated?
Moving a pointer is the same as copying it. So no invalidation is going on, neither should it be going on. If you want a move constructor that leaves the moved-from object in a particular state (i.e. sets a pointer data member to nullptr), you have to write your own - or delegate this responsibility to some smart pointer class such as std::unique_ptr.
Notice, that the word "invalidated" is not quite correct here. Move constructors (as well as move assignment operators) are meant to leave the moved-from object in a valid (yet unspecified) state.
In other words, the class invariant needs to be respected - and it should be possible to invoke on a moved-from objects operations that do not have any precondition on its state (usually, destruction and assignment).
does std::is_move_constructible::value == true implies that T has a usable move constructor?
No. It states that you can take an rvalue expression of the object type and construct an object from it. Whether this uses the move constructor or the copy constructor is not relevant to this trait.
is f2 move constructed ?
Yes.
if so, shouldn't the rvalue be invalidated?
No. That's not how movement works.
how can I know if instances of a class can be properly move-constructed(invalidate the old one)?
That is not any definition of "properly move-constructed" that exists. If you want to "invalidate the old one", then you will have to do that yourself.
Move construction generally guarantees nothing about the state of the old object. It will be in a valid but undefined state. Such state very much can be "the same as it was before". Move construction for a pointer is the same as copying the pointer.
If you want to "invalidate" after a move, then you need to write your own move constructor that explicitly does that.
(I'm using VS11)
Then you have no compiler-generated move constructors at all. Not that it would matter, since the move and copy constructors for pointers both do the same thing.
the default behaviour of move constructor is same as a copy
constructor, is it correct? if it's true
No. It's wrong. It's true only for primitives. It's similar to that of copy constructor.
The default generated copy constructor calls the copy constructor of all its members in the declared order
But The default generated move constructor calls the move constructor of all its members in the declared order
Now the next question is, what is the copy/move constructor of the primitives ints floats pointers do?
Answer: They just copy the values (both copy and move constructor)
Note that Visual Studio 2012 / VC++11 does not support compiler generated move constructors; in fact, consider this quote from "C++11 Features in Visual C++ 11" blog post (emphasis mine):
Rvalue references v3.0 adds new rules to automatically generate move
constructors and move assignment operators under certain conditions.
This will not be implemented in VC11, which will continue to follow
VC10's behavior of never automatically generating move
constructors/move assignment operators.
With raw pointers, you have to define move constructors by yourself, manually clearing the old "moved-from" pointer:
class Foo
{
public:
// Move constructor
Foo(Foo&& other)
: m_ptr(other.m_ptr) // copy pointer value
{
// Clear out old "moved-from" pointer, to avoid dangling references
other.m_ptr = nullptr;
}
private:
int* m_ptr;
};
Instead, if you use a smart pointer like std::unique_ptr, move constructor is properly defined, and you can just call std::move:
class Foo
{
public:
// Move constructor
Foo(Foo&& other)
: m_ptr(std::move(other.m_ptr)) // move from other,
// old pointer automatically cleared
{
}
private:
std::unique_ptr<int> m_ptr;
};
With automatically generated move constructors, you don't have to define a custom move constructor explicitly, if member-wise move is OK for you.
n3376 12.8/15
The implicitly-defined copy/move constructor for a non-union class X performs a memberwise copy/move
of its bases and members.
Each base or non-static data
member is copied/moved in the manner appropriate to its type:
— if the member is an array, each element is direct-initialized with the corresponding subobject of x;
— if a member m has rvalue reference type T&&, it is direct-initialized with static_cast(x.m);
— otherwise, the base or member is direct-initialized with the corresponding base or member of x.
if foo has a move constructor, then wouldn't foo f2(std::move(f)) calls it?
You do not get the default move constructor when you supply your copy constructor. Add following line to get it ( and notice the change ).
foo(foo&& ifm)=default;

Why isn't object returned by std::move destroyed immediately

I tested the following code:
#include <iostream>
using namespace std;
class foo{
public:
foo() {cout<<"foo()"<<endl;}
~foo() {cout<<"~foo()"<<endl;}
};
int main()
{
foo f;
move(f);
cout<<"statement \"move(f);\" done."<<endl;
return 0;
}
The output was:
foo()
statement "move(f);" done.
~foo()
However, I expected:
foo()
~foo()
statement "move(f);" done.
According to the source code of the function move:
template<typename _Tp>
constexpr typename std::remove_reference<_Tp>::type&&
move(_Tp&& __t) noexcept
{ return static_cast<typename std::remove_reference<_Tp>::type&&>(__t); }
The returned object is a right value, So Why isn't it destroyed immediately?
-----------------------------------------------------------------
I think I just confused rvalue and rvalue reference.
I modified my code:
#include <iostream>
template<typename _Tp>
constexpr typename /**/std::remove_reference<_Tp>::type /* no && */
/**/ mymove /**/ (_Tp&& __t) noexcept
{ return static_cast<typename std::remove_reference<_Tp>::type&&>(__t); }
using namespace std;
class foo{
public:
foo() {cout<<"foo() at "<<this<<endl;} /* use address to trace different objects */
~foo() {cout<<"~foo() at "<<this<<endl;} /* use address to trace different objects */
};
int main()
{
foo f;
mymove(f);
cout<<"statement \"mymove(f);\" done."<<endl;
return 0;
}
And now I get what I've been expecting:
foo() at 0x22fefe
~foo() at 0x22feff
statement "mymove(f);" done.
~foo() at 0x22fefe
Moving from an object doesn't change its lifetime, only its current value. Your object foo is destroyed on return from main, which is after your output.
Futhermore, std::move doesn't move from the object. It just returns an rvalue reference whose referand is the object, making it possible to move from the object.
Objects get destroyed when they go out of scope. Moving from an object doesn't change that; depending on what the move constructor or move assignment operator does, the state of the object can be different after the move, but is hasn't yet been destroyed, so the practical rule is that moving from an object must leave it in a state that can be destroyed.
Beyond that, as #R.MartinhoFernandes points out, std::move doesn't do anything. It's the object's move constructor or move assignment operator that does whatever needs to be done, and that isn't applied in a call to std::move; it's applied when the moved-from object is used to construct a new object (move constructor) or is assigned to an existing object (move assignment operator). Like this:
foo f;
foo f1(f); // applies foo's copy constructor
foo f2(std::move(f)); // applies foo's move constructor
foo f3, f4;
f3 = f; // applies foo's copy assignment operator
f4 = std::move(f1); // applies foo's move assignment operator
A std::move doesn't change objects lifetime. Roughly speaking it's nothing more than a static_cast that casts a non const lvalue to a non const rvalue reference.
The usefulness of this is overload resolution. Indeed, some functions take parameters by const lvalue reference (e.g. copy constructors) and other take by non const rvalue reference (e.g. move constructors). If the passed object is a temporary, then the compiler calls the second overload. The idea is that just after the function is called the a temporary can no longer be used (and will be destroyed). Therefore the second overload could take ownership of the temporary's resources instead of coping them.
However, the compiler will not do it for a non-temporary object (or, to be more correct for an lvalue). The reason is that the passed object has a name that remains in scope and therefore is alive could still be used (as your code demonstrate). So its internal resources might still be required and it would be a problem if they have had been moved to the other object. Nevertheless, you can instruct the compiler that it can call the second overload by using std::move. It casts the argument to a rvalue reference and, by overload resolution, the second overload is called.
The slight changed code below illustrate this point.
#include <iostream>
using namespace std;
class foo{
public:
foo() { cout << "foo()" << endl; }
~foo() { cout << "~foo()" << endl; }
};
void g(const foo&) { cout << "lref" << endl; }
void g(foo&&) { cout << "rref" << endl; }
int main()
{
foo f;
g(f);
g(move(f));
// f is still in scope and can be referenced.
// For instance, we can call g(f) again.
// Imagine what would happen if f had been destroyed as the question's author
// originally though?
g(static_cast<foo&&>(f)); // This is equivalent to the previous line
cout<<"statement \"move(f);\" done."<<endl;
return 0;
}
The output is
foo()
lref
rref
rref
statement "move(f);" done.
~foo()
Update: (After the question has been changed to use mymove)
Notice that the new code doesn't give exactly what you said at the very beginning. Indeed it reports two calls to ~foo() rather than one.
From the displayed addresses we can see that the original object of type foo, namely, f is destroyed at the very end. Exactly as it used to be with the original code. As many have pointed out, f is destroyed only at the end of its scope (the body of function main). This is still the case.
The extra call to ~foo() reported just before the statement "mymove(f);" done. destroys another object which is a copy of f. If you add a reporting copy constructor to foo:
foo(const foo& orig) { cout << "copy foo from " << &orig << " to " << this << endl;}
Then you get an output similar to:
foo() at 0xa74203de
copy foo from 0xa74203de to 0xa74203df
~foo() at 0xa74203df
statement "move(f);" done.
~foo() at 0xa74203de
We can deduce that calling mymove yields a call to the copy constructor to copy f to another foo object. Then, this newly created object is destroyed before execution reaches the line that displays statement "move(f);" done.
The natural question now is where this copy come from? Well, notice the return type of mymove:
constexpr typename /**/std::remove_reference<_Tp>::type /* no && */`
In this example, after a simplification for clarity, this boils down to foo. That is, mymove returns a foo by value. Therefore, a copy is made to create a temporary object. As I said before, a temporary is destroyed just after the expression that creates it finishes to be evaluated (well, there are exceptions to this rule but they don't apply to this code). That explains the extra call to ~foo().
Because in the general case, the move could happen in another translation unit. In your example, the object wasn't even moved, only marked as movable. This means that the caller of std::move will not know if the object was moved or not, all he knows is, that there is an object and that it has to call the destructor at the end of the scope/lifetime of that object. std::move only marks the object as movable, it does not perform the move operation or create a moved copy that can be further moved or anything like that.
Consider:
// translation unit 1
void f( std::vector< int >&& v )
{
if( v.size() > 8 ) {
// move it
}
else {
// copy it as it's just a few entries
}
}
// translation unit 2
void f( std::vector< int >&& );
std::vector< int > g();
int main()
{
// v is created here
std::vector< int > v = g();
// it is maybe moved here
f( std::move( v ) );
// here, v still exists as an object
// when main() ends, it will be destroyed
}
In the example above, how would translation unit 2 decide whether or not to call the destructor after the std::move?
You're getting confused by the name -- std::move doesn't actually move anything. It just converts (casts) an lvalue reference into an rvalue reference, and is used to make someone else move something.
Where std::move is useful is when you have an overloaded function that takes either an lvalue or an rvalue reference. If you call such a function with a simple variable, overload resolution means you'll call the version with the lvalue reference. You can add an explicit call to std::move to instead call the rvalue reference version. No moves involved, except within the function that takes the rvalue reference.
Now the reason it is called move is the common usage where you have two constructors, one of which takes an lvalue reference (commonly called the copy constructor) and one that takes an rvalue reference (commonly called the move constructor). In this case, adding an explicit call to std::move means you call the move constructor instead of the copy constructor.
In the more general case, it's common practice to have overloaded functions that take lvalue/rvalue references where the lvalue version makes a copy of the object and the rvalue version moves the object (implicitly modifying the source object to take over any memory it uses).
Suppose code like this:
void bar(Foo& a) {
if (a.noLongerneeded())
lastUnneeded = std::move(a);
}
In this case, the caller of bar can not know that the function might in some cases end up calling the destructor of the passed object. It will feel responsible for that object, and make sure to call its destructor at any later point.
So the rule is that move may turn a valid object into a different but still valid object. Still valid means that calling methods or the destructor on that object should still yield well-defined results. Strictly speaking, move by itself does nothing except tell the receiver of such a reference that it may change the object if doing so makes sense. So it's the recipient, e.g. move constructor or move assignment operator, which does the actual moving. These operations will usually either not change the object at all, or set some pointers to nullptr, or some lengths to zero or something like that. They will however never call the destructor, since that task is left to the owner of the object.