问题
How is the proper way to implement move semantics with operator+
? Similarly to how it works for std::string
?
I have attempted the following, however I was hoping there was some more elegant and possibly more correct way to do it:
class path
{
std::vector<std::string> path_;
public:
path& path::operator+=(const path& other)
{
path_.insert(std::begin(path_), std::begin(other.path_), std::end(other.path_));
return *this;
}
path& path::operator+=(path&& other)
{
path_.insert(std::begin(path_), std::make_move_iterator(std::begin(other.path_)), std::make_move_iterator(std::end(other.path_)));
return *this;
}
};
template<typename L, typename R>
typename std::enable_if<std::is_convertible<path, L>::value, path>::type operator+(const L& lhs, const R& rhs)
{
auto tmp = std::forward<L>(lhs);
tmp += std::forward<R>(rhs);
return tmp;
}
回答1:
Way too complicated. :) Just abide by the rule you already should've followed:
- Take the lhs of
operator+
by value - implement
operator+
in terms ofoperator+=
on the lhs
This was already true in C++03, because of copy elision and RVO. The rule of thumb: If you make a copy anyways, make it in the parameters.
With that in mind:
#include <iterator>
#include <utility>
class path
{
std::vector<std::string> path_;
public:
path& operator+=(path other)
{
auto op_begin = std::make_move_iterator(std::begin(other.path_));
auto op_end = std::make_move_iterator(std::end(other.path_));
path_.reserve(path_.size() + other.path_.size());
path_.insert(std::end(path_), op_begin, op_end);
return *this;
}
};
path operator+(path lhs, path rhs)
{
return std::move(lhs += std::move(rhs));
}
This should be the most optimal form. Note that I also changed your operator+=
to actually append the path, and not prepend (I hope that's what you had in mind. If not, feel free to change it to std::begin(path_)
again).
I also made the rhs of operator+
and operator+=
values and then just moved them around. std::make_move_iterator
is also a nice utility. As the name implies, instead of copying, it moves the pointed-at elements. This should really be as fast as it's going to get.
Another version might be to use the iterator version of std::move
in operator+=
:
path& operator+=(path other)
{
path_.reserve(path_.size() + other.path_.size());
std::move(other.begin(), other.end(), std::back_inserter(path_));
return *this;
}
来源:https://stackoverflow.com/questions/8687701/operator-and-move-semantics