Reputation: 2365
I want to move a large container from a return value into another class using that class' constructor. How do I formulate the parameter to ensure that it doesn't end up being copied?
/* for the sake of simplicity, imagine this typedef to be global */
typedef std::unordered_map<std::string, unsigned int> umap;
umap foo()
{
umap m; /* fill with lots of data */
return m;
}
class Bar
{
public:
Bar(umap m) : bm(m) { }
private:
umap bm;
};
Bar myBar(foo()); // run foo and pass return value directly to Bar constructor
Will above formulation trigger the appropriate behavior, or do I need to specify the constructor's parameters as rvalue-references, the way containers do for their own move-semantics?
public:
Bar(umap&& m) : bm(m) { }
or
public:
Bar(umap&& m) : bm(std::move(m)) { }
...?
Upvotes: 13
Views: 3211
Reputation: 25613
In addition to the answers above:
What I see in your example is a classical case for "copy elision" optimization.
What are copy elision and return value optimization?
An actual compiler needs no support from you to optimize the things away and there is no must to deal yourself with rvalue references in this special case of return value optimization.
Upvotes: 1
Reputation: 210525
In C++11 you use std::move
. If you need C++03 compatibility you can default-construct bm
via :bm()
and then use using std::swap; swap(bm, m);
.
Upvotes: 0
Reputation: 7904
Enforce passing of rvalue into the constructor and also make sure to move the map, like this:
Bar(umap &&m) : bm(std::move(m)) {}
Let's start with the first example of Bar(umap m) : bm(m) {}
. This will perform at least 1 copy, maybe 2.
bm(m)
will always copy. m
here is lvalue even though it's bound to an rvalue. To make it move, you must wrap it in std::move
to turn it back into an rvalue. We get bm(std::move(m))
.
Bar(umap m)
might copy. The value will be moved in if it's an rvalue, but will be copied if it's an lvalue. Since we want to prevent all copies, we need to only let rvalue bind. We get Bar(umap && m)
.
Upvotes: 4
Reputation: 477140
If you want to support moving and copying, the easiest way would be to pass by value:
Bar(umap m) : bm(std::move(m)) { }
Now you can construct Bar
from both an lvalue and an rvalue:
umap m;
Bar b1(m); // copies
Bar b2(std::move(m)); // moves
Bar b3(make_umap()); // moves
If you only want to support rvalues, then use an explicit rvalue reference:
Bar(umap && m) : bm(std::move(m)) { }
The std::move
is always necessary, since (m)
is always an lvalue.
Upvotes: 16