Perfect forwarding and the scope of temporary objects - c++

In an attempt to realize perfect forwarding for the * operator I constructed the following example.
#include <string>
#include <iostream>
class A {
public:
std::string name;
A(const A& _other) : name(_other.name) {
std::cout << "Copy-Construct with name: " << name << std::endl;
}
A(A&& _other) : name(std::move(_other.name)) {
std::cout << "Move-Construct with name: " << name << std::endl;
}
A(std::string _name): name(_name) { }
};
A operator*(const A& _lhs, const A& _rhs) {
std::cout << "Start Operator Copy with: " << _lhs.name << " " << _rhs.name << std::endl;
A bla(_lhs.name+" "+_rhs.name);
return bla;
}
A&& operator*(A&& _lhs, const A& _rhs) {
std::cout << "Start Operator Move with: " << _lhs.name << " " << _rhs.name << std::endl;
_lhs.name += " "+_rhs.name;
return std::move(_lhs);
}
int main() {
A a("a");
A b("b");
A c("c");
A d("d");
A x = a*b*A("t1")*c*A("t2")*A("t3")*d;
std::cout << "Final result is: " << x.name << std::endl;
}
The result is as I hoped, in particular only one move constructor and no copy constructor is called.
Start Operator Copy with: a b
Start Operator Move with: a b t1
Start Operator Move with: a b t1 c
Start Operator Move with: a b t1 c t2
Start Operator Move with: a b t1 c t2 t3
Start Operator Move with: a b t1 c t2 t3 d
Move-Construct with name: a b t1 c t2 t3 d
Final result is: a b t1 c t2 t3 d
Now my question is: Is this leagal C++11 code? In particular can I rely on the fact that the first temporary object (contructed from a and b) leaves its scope at the semicolon and not before that? And is the construction, to return an object obtained as a move reference back as a move reference, legal at all?

A&& operator*(const A& _lhs, const A& _rhs) {
std::cout << "Start Operator Copy with: " << _lhs.name << " " << _rhs.name << std::endl;
A* bla = new A(_lhs.name+" "+_rhs.name);
return std::move(*bla);
}
This creates a dynamically allocated object, so the caller is responsible for deleting it. Your example fails to do that, so leaks memory. This is a terrible function. It should return by value instead, which will be faster because you don't allocate the object on the heap.
A&& operator*(A&& _lhs, const A& _rhs) {
std::cout << "Start Operator Move with: " << _lhs.name << " " << _rhs.name << std::endl;
_lhs.name += " "+_rhs.name;
return std::move(_lhs);
}
This doesn't lead to memory leaks, so isn't totally obviously wrong like the first one, but it's still wrong. If you call it with a temporary object it returns a reference to that same temporary, but that can lead to dangling references:
A&& c = A("a") * A("b");
The reference c is bound to the temporary created by A("a") but that does out of scope at the end of the statement. Any attempt to use c has undefined behaviour.
Both overloads should return by value.
You might also want an overload for the case where the left-hand side is an lvalue and the right-hand side is an rvalue, as that allows you to re-use the right-hand side object. And if you add that you also need an overload for the case where both operands are rvalues. Basically, look at how std::string defines operator+

Related

vector move operation vs element move operation

For the following example, why the vector move operation is not triggered? How do I know when I should explicitly use a move operator?
#include <iostream>
#include <vector>
using namespace std;
class Test {
public:
Test() {
std::cout << " default " << std::endl;
}
Test(const Test& o) {
std::cout << " copy ctor " << std::endl;
}
Test& operator=(const Test& o) {
std::cout << " copy assign " << std::endl;
return *this;
}
Test(Test&& o) {
std::cout << " move ctor" << std::endl;
}
Test& operator=(Test&& o) {
std::cout << " move assign " << std::endl;
return *this;
}
};
int main()
{
std::cout << " vector: " << std::endl;
std::vector<Test> p;
p = {Test()}; // expect vector move here since the RHS is temporary.
std::cout << std::endl;
std::cout << " single value " << std::endl;
Test tt;
tt = Test();
}
Output:
vector:
default
copy ctor
single value
default
default
move assign
I was under the impression that when we assign a temporary variable to a lvalue (the single value case in the example), it would trigger a move operation if it exists. Seems that I was wrong and my understanding was overly simplified, I need to carefully check case by case to ensure there's no redundant copy.
std::vector has an assignment operator that takes an std::initializer_list:
vector& operator= (initializer_list<value_type> il);
So when you wrote p = {Test()}; you're actually using the above assignment operator.
Now why a call to the copy constructor is made can be understood from dcl.init.list, which states:
An object of type std::initializer_list<E> is constructed from an initializer list as if the implementation allocated a temporary array of N elements of type const E, where N is the number of elements in the initializer list. Each element of that array is copy-initialized with the corresponding element of the initializer list, and the std::initializer_list object is constructed to refer to that array.

Change constructor order of operations?

I have the following sample code, and the copy assignment is doing something I don't want - it's first constructing the new samp(6), then copying it to z, then destroying the new samp(6) it built. Is there a way to change the constructors such that = acts like a pointer, destroying the originally constructed samp(5) and replacing it with the new samp(6), calling the destructor on samp(5), rather than samp(6)?
#include <iostream>
class samp
{
public:
samp(int a)
{
m_a = a;
std::cout << "cons" <<m_a << std::endl;
}
int m_a;
samp(const samp& other)
{
std::cout << "copy" << m_a << std::endl;
m_a = other.m_a;
}
samp& operator= (const samp& other)
{
std::cout << "assg" << m_a << std::endl;
samp* z =new samp(other.m_a);
return *z;
}
~samp()
{
std::cout << "dest" <<m_a<< std::endl;
}
};
int main()
{
samp z(5);
z = samp(6);
std::cout << z.m_a << std::endl;
return 0;
}
Maybe pointer semantics is what you want:
#include <memory>
// ...
auto z = std::make_unique<samp>(5);
z = std::make_unique<samp>(6); // dest5
std::cout << z->m_a << '\n'; // 6
Although if you are coming to C++ from a language where object names are object references, it may be better to get used to C++ value semantics instead of trying to replicate object references :)
operator= is a member of your object so the this pointer is available. Also, assignment means the target of the assignment should change. Your version is creating a new object, but leaving the target alone. See if this does what you want:
samp& operator= (const samp& other)
{
m_a = other.m_a;
return *this;
}

using std::move to prevent copying

I have the following code:
#include <iostream>
#include <vector>
struct A
{
std::vector<int> x;
A()
{
std::cout << "A()" << std::endl;
}
A(const A&)
{
std::cout << "A(const A&)" << std::endl;
}
~A()
{
std::cout << "~A()" << std::endl;
}
};
struct B : public A
{
std::vector<int> y;
B()
{
std::cout << "B()" << std::endl;
}
B(const A&a)
{
std::cout << "B(const A&)" << std::endl;
x = std::move(a.x);
y.resize(x.size());
}
B(const A&&a)
{
std::cout << "B(const A&&)" << std::endl;
x = std::move(a.x);
y.resize(x.size());
}
B(const B&)
{
std::cout << "B(const B&)" << std::endl;
}
~B()
{
std::cout << "~B()" << std::endl;
}
};
A ret_a()
{
A a;
a.x.resize(10);
return a;
}
int main()
{
std::cout << "section I" << std::endl << std::endl;
A a = ret_a();
B b(a);
std::cout << "a.x.size=" << a.x.size() << std::endl;
std::cout << std::endl << "section II" << std::endl << std::endl;
B b2(ret_a());
std::cout << "b.x.size=" << b.x.size() << std::endl;
std::cout << std::endl << "section III" << std::endl << std::endl;
return 0;
}
With output (VS2013, Release build)
section I
A()
A()
B(const A&)
a.x.size=10
section II
A()
A()
B(const A&&)
~A()
b.x.size=10
section III
~B()
~A()
~B()
~A()
~A()
Why a.x.size() within "section I" has size 10? I thought that std::move should move all data from a.x to y.x
Why did "section II" call constructor A() twice? I thought that B(const A&&) would prevent excessive copying of A
UPDATE
see fixed code at http://pastebin.com/70Nmt9sT
T&& and const T&& are not the same type. You almost never want a const rvalue reference - you can't steal its resources since you made it const! x = std::move(a.x); in B(const A&a) copies a.x since the return type of std::move(a.x) is const vector<int>&&.
The constructor, B(const A&&) calls the default constructor of A since it is derived from A, and the member initializer list does not make an attempt to construct the base A. This is the second A call.
Why a.x.size() within "section I" has size 10? I thought that std::move should move all data from a.x to y.x
This is because of B(const A&& a). Since a is const within that constructor, you only have const access to its member x, and calling std::move on a vector<T> const results in a vector<T> const&& which cannot bind to vector's move constructor (which takes a vector<T>&& argument). Instead it ends up calling the copy constructor, which leaves the source object unmodified.
Why did "section II" call constructor A() twice? I thought that B(const A&&) would prevent excessive copying of A
The first default construction occurs within the body of ret_a(). The second default construction is that of the A sub-object of B. To avoid the second one move the A instance in the member initializer list.
B(const A&&a)
: A(std::move(a))
{
std::cout << "B(const A&&)" << std::endl;
y.resize(x.size());
}
Note that the move doesn't actually result in moving the contents of a due to the same reason as explained above. Moreover, even modifying the signature to B(A&& a) would not result in the contents of a being moved because the user provided copy constructor and destructor definitions prevent implicit generation of a move constructor for A, and it'll be copied instead.

const value and RVO

Say I have this function:
template <class A>
inline A f()
{
A const r(/* a very complex and expensive construction */);
return r;
}
Is it a good idea to declare r const, since a const variable cannot be moved? Note that the returned value is not const. The qualm I am grappling is, that r truly is const, but it may not be a good idea to declare it as such. Yet the qualifier should be helping the compiler generate better code.
As demonstrated here, NRVO elides the copy of r implied by the line return r;
#include <iostream>
struct A {
const char* name;
A( const char* name_ ):name(name_) { std::cout << "created " << name << "\n"; }
A(A const&){ std::cout << "copied " << name << "\n"; }
A(A &&){ std::cout << "moved " << name << "\n"; }
};
A f() {
std::cout << "start of f()\n";
A const r("bob");
std::cout << "body of f()\n";
return r;
}
int main() {
A x = f();
}
And the copy in main is also elided.
If you block NRVO and RVO in some other way (for instance using the flag -fno-elide-constructors when compiling with GCC), the const can cause your object to be copied instead of moved. You can see this if we remove the copy constructor from A:
#include <iostream>
struct A {
const char* name;
A( const char* name_ ):name(name_) { std::cout << "created " << name << "\n"; }
//A(A const&){ std::cout << "copied " << name << "\n"; }
A(A &&){ std::cout << "moved " << name << "\n"; }
};
A f() {
std::cout << "start of f()\n";
A const r("bob");
std::cout << "body of f()\n";
return r;
}
int main() {
A x = f();
}
the code no longer compiles. While the copy constructor isn't executed so long as NRVO occurs, its existence is required by your const local variable.
Now, NRVO requires a few things, such as a single variable which is returned along every single execution path of the function in question: if you ever "abort" and do a return A(), NRVO is blocked, and your const local variable suddenly forces a copy at all return sites.
If class A is under your control, and you want to return const objects by move, you can do
mutable bool resources_were_stolen = false;
and set that to true in a const move constructor
A(const A&& other) { ...; other.resources_were_stolen = true; }
~A() { if (!resources_were_stolen) ... }
Actually, the destructor probably would become if (resources_were_stolen) some_unique_ptr.release();, using the fact that objects lose their const-ness during construction and destruction.

Returning pointer by value does not move the object

I have compiled this code with vs2011. It prints first constructor then copy constructor.
But if I change the function to return a instead of ap, it will move the object. Is this a bug or why does it behave like this? Is *ap not a rvalue?
struct A
{
A() { cout << "constructor" << endl;}
A(const A&) { cout << "copy constructor " << endl;}
void operator=(const A&) { cout << "assignment operator" << endl; }
A( A&&) { cout << "move copy constructor" << endl;}
void operator=(A&&) { cout << "move assignment operator" << endl;}
};
A func() { A a; A *ap = &a; return *ap; }
int main()
{
A a = func();
return 0;
}
*ap is an lvalue (ยง 5.3.1.1, n3290) which is in general not safe for the move to happen automatically. The local variable return a; is a different case. There's no requirement for the compiler to prove that in this specific instance it would be safe. This is another good reason for not using pointers in cases where you don't really want pointer semantics.
Changing it to:
return std::move(*ap);
will cause it to be explicitly moved however.