Reputation: 916
I have a few methods that I need to give them ability to take variable by const lvalue (where it would be copied) and rvalue reference (for speed)
struct Object {
...
Object(Object&& o) { ... }
Object(const Object& o) { ... }
...
};
...
struct SomeClass {
...
Object arr[7];
void copy(int pos,const Object& o) { arr[pos] = o; }
void copy(int pos,Object&& o) { arr[pos] = o; }
...
};
So, two copy methods are COMPLETELY IDENTICAL in SomeClass. The only difference is that in one Object is passed as const, which will be copied, and in another Object is passed to be consumed for a quick copy as rvalue reference.
The code of these two methods is COMPLETELY IDENTICAL.
Now, it is not that tragic in the example above, however, I have methods that are a little larger, 9-15 lines or so. The workaround is, obviously, to copy them like it was done here, but it doesn't feel right.
How can I reuse the code of copy method?
Upvotes: 2
Views: 989
Reputation: 16737
Universal references and std::forward
allow you to implement perfect forwarding. Here's the modified copy
:
template <typename T>
void copy(int pos, T&& o) {arr[pos] = std::forward<T>(o);}
If you are worried about possible implicit conversions from other types to Object
, you can also add a static_assert
in the body of copy
. If you pass an l-value Object
to copy
, T
will be deduced to be Object&
. std::forward<Object&>
returns a reference. So, the copy assignment overload will be chosen. If you pass an r-value Object
to copy
, T
will be deduced to be Object
. std::forward<Object&>
returns Object&&
. Since std::forward<Object>(o)
is an rvalue as well, the move assignment operator will get picked.
Upvotes: 3
Reputation: 137320
First, you are doing a copy assignment in both cases:
void copy(int pos,const Object& o) { arr[pos] = o; }
void copy(int pos,Object&& o) { arr[pos] = o; }
If it has a name, it's a lvalue. In the second function, o
has a name, so it's a lvalue (despite being a rvalue reference). You want arr[pos] = std::move(o);
.
The usual way to avoid having to write copy
twice is to take o
by value and move from it:
void copy(int pos, Object o) { arr[pos] = std::move(o); }
o
will be copy constructed if you pass a lvalue, and then move-assigned into arr[pos]
. If you pass a rvalue, o
will be move constructed and then further move-assigned into the array. So you still avoid a copy in the rvalue case, but you pay for an extra move in both cases. If moves are cheap like they should be, it's not much extra overhead.
However, note that this does not work well with legacy types that do not support move semantics. In that case, this function will make a copy of what you passed and then copy-assign from that, while the two overloads taking references will only do one copy.
Upvotes: 3