When you deal with move semantics and pointers, you have to understand what is actually happening.
What move semantics and r-value references mean is:
"This is an object that cannot be named which is about to die soon"
What a pointer means is:
"This is a named object"
So a pointer is at odds with the concept of move semantics in the first place.
So the simplest thing to do is get rid of the pointer from the append parameters. It is neater to use a reference for the first parameter, and you can just not use anything for the second.
static void append(CTest &dst, CTest src)
{
TRACE("append\n");
}
But you are probably thinking, "wait, this still won't cause it to use the move constructor" and that is true. Calling this with:
CTest::append(tst, tst2);
will result in a compiler error, but:
CTest::append(tst, std::move(tst2));
actually goes through the move constructor.
But there are always problems doing this. Move construction and r-value references are only ever intended to work on objects with no name, like temporary objects, to move the contents to a live object before it goes out of scope. Doing this to an object that has a name is dangerous, after you use std::move (or the related static cast) you must assume that the object that you moved from is dead and treat it with care. Just letting it go out of scope without accessing it is the best option or do a full reinitialisation of the object. It is possible to write the move constructor in such a way that it will leave the object in a good state, but this can be good.
The better option is to us a function to return the object that you want to move from.
CTest obtain_test()
{
return CTest();
}
This can then be used like:
int wmain()
{
CTest tst;
CTest::append(tst, obtain_test());
return 0;
}
It also guarantees that you can never inadvertently access the object you moved from.
In general, while std::move can be used in this way, one of the big reasons to use it is to add back move capabilities to an r-value reference. For example, suppose you have the classes:
class test_base
{
//
test_base(test_base &&other)
{
}
//
};
class test_derived : public test_base
{
//
test_derived(test_derived &&other) : test_base(other) //surprise, this calls the copy constructor
{
}
//
};
You may set it up like above and think the derived move constructor will call the base move constructor. But it doesn't, it actually calls the copy constructor. The reason for this is because you have now named the object and is no longer an unnamed object that is about to go out of scope. From this point on, the compiler treats it as an l-value reference. In order to get this to call the base move constructor you have to use std::move:
test_derived(test_derived &&other) : test_base(std::move(other))
{
}
This is one of the biggest reasons for std::move. This should also show that you need to set your parameters up in a certain way for the move constructor to actually be called.