I am trying to compose an illustrative example which shows how to implement move semantics on an object that will be stored in a vector
.
Please consider the following code, which is my illustrative example so far. It is designed to be a canonical, pedantically correct implementation of an object that implements move semantics, does not implement copy semantics, and can be stored in a vector<>
(Where T is Moveable, below). How did I do?
#include <string>
#include <iostream>
#include <vector>
#include <list>
#include <algorithm>
#include <memory>
#include <iterator>
using namespace std;
class Moveable
{
public:
string foo_;
string bar_;
Moveable(Moveable&& rhs) : foo_(std::move(rhs.foo_)), bar_(std::move(rhs.bar_)) {} // move construction
Moveable(const string& foo) : foo_(foo) {}; // convert construction
Moveable& operator=(Moveable&& rhs) // move assignment
{
foo_ = std::move(rhs.foo_);
bar_ = std::move(rhs.bar_);
return * this;
}
private:
Moveable(const Moveable&); // not defined, not copy-constructible
Moveable& operator=(const Moveable&); // not defined, not copy-assignable
Moveable(); // not defined, not default constructible
};
Moveable generate_it()
{
static string foo ;
if( foo.empty() || foo[0] == 'z' )
foo.insert(0, 1, 'a');
else
foo[0]++;
return foo;
}
int main()
{
typedef vector<Moveable> Moveables;
Moveables v;
generate_n(back_inserter(v), 1024, &generate_it);
cout << v.size() << endl;
string target = "zzz";
auto that = find_if(v.begin(), v.end(), [target](const Moveables::value_type& it) -> bool
{
return it.foo_ == target;
});
if( that != v.end() )
v.erase(that);
sort(v.begin(), v.end(), [](const Moveables::value_type& lhs, const Moveables::value_type& rhs) -> bool
{
return lhs.foo_ > rhs.foo_;
});
cout << v.size() << endl;
}
1 Answer 1
One small error: Mark the move constructor as
noexcept
, otherwise there are situations where it won’t be used. The linked case is different since the class has a copy-constructor, yet I can imagine that there are still situations where it matters, especially since your copycon isn’t deleted, just private and undefined.Why is the destructor virtual? If the example is supposed to be minimal then this might be distracting.
In
generate_it
:return std::move(Moveable(foo));
The
std::move
here is redundant, since you are returning a temporary. What’s more, the explicit constructor call is redundant too, since the constructor isn’t marked asexplicit
. Justreturn foo;
will do.The
find_if
call could be replaced by vanillafind
, using temporary construction again:auto that = find(v.begin(), v.end(), target);
Finally, in the
sort
call, why aren’t the arguments declaredconst&
? Granted, makes the line even longer as it stands this is inconsistent with the const-correctness illustrated in thefind_if
call.
-
\$\begingroup\$ +1: various replies. 1) The destructor was an artifact of my testing durig development. Since its implementation was trivial, and the tor was public, I've removed it entirely. \$\endgroup\$John Dibling– John Dibling2012年06月26日 16:35:19 +00:00Commented Jun 26, 2012 at 16:35
-
\$\begingroup\$ 2)
noexcept
is not supported by MSVC10. Sad. \$\endgroup\$John Dibling– John Dibling2012年06月26日 16:38:41 +00:00Commented Jun 26, 2012 at 16:38 -
\$\begingroup\$ 3) Fixed
generate_it
4) fixedsort
call. These were simply part of the harness, but it's important that the whole thing is correct. \$\endgroup\$John Dibling– John Dibling2012年06月26日 16:40:33 +00:00Commented Jun 26, 2012 at 16:40
unique_ptr
which ultimately was orthogonal to what I was really going for. \$\endgroup\$