Mots clés : c++c++-faqc++11move-semanticsc++
90
#include <cstring> #include <algorithm> class string { char* data; public: string(const char* p) { size_t size = std::strlen(p) + 1; data = new char[size]; std::memcpy(data, p, size); }
~string() { delete[] data; } string(const string& that) { size_t size = std::strlen(that.data) + 1; data = new char[size]; std::memcpy(data, that.data, size); }
string a(x); // Line 1 string b(x + y); // Line 2 string c(some_function_returning_a_string()); // Line 3
string(string&& that) // string&& is an rvalue reference to a string { data = that.data; that.data = nullptr; }
string& operator=(string that) { std::swap(data, that.data); return *this; } };
85
class cannot_benefit_from_move_semantics { int a; // moving an int means copying an int float b; // moving a float means copying a float double c; // moving a double means copying a double char d[64]; // moving a char array means copying a char array // ... };
{ std::auto_ptr<Shape> a(new Triangle); // ... // arbitrary code, could throw exceptions // ... } // <--- when a goes out of scope, the triangle is deleted automatically
auto_ptr<Shape> a(new Triangle); +---------------+ | triangle data | +---------------+ ^ | | | +-----|---+ | +-|-+ | a | p | | | | | +---+ | +---------+ auto_ptr<Shape> b(a); +---------------+ | triangle data | +---------------+ ^ | +----------------------+ | +---------+ +-----|---+ | +---+ | | +-|-+ | a | p | | | b | p | | | | | +---+ | | +---+ | +---------+ +---------+
auto_ptr(auto_ptr& source) // note the missing const { p = source.p; source.p = 0; // now the source no longer owns the object }
auto_ptr<Shape> a(new Triangle); // create triangle auto_ptr<Shape> b(a); // move a into b double area = a->area(); // undefined behavior
auto_ptr<Shape> make_triangle() { return auto_ptr<Shape>(new Triangle); } auto_ptr<Shape> c(make_triangle()); // move temporary into c double area = make_triangle()->area(); // perfectly safe
auto_ptr<Shape> variable(expression); double area = expression->area();
auto_ptr<Shape> c(make_triangle()); ^ the moved-from temporary dies right here
lvalue const lvalue rvalue const rvalue --------------------------------------------------------- X& yes const X& yes yes yes yes X&& yes const X&& yes yes
void some_function(std::string&& r); some_function("hello world");
template<typename T> class unique_ptr { T* ptr; public: T* operator->() const { return ptr; } T& operator*() const { return *ptr; }
explicit unique_ptr(T* p = nullptr) { ptr = p; } ~unique_ptr() { delete ptr; }
unique_ptr(unique_ptr&& source) // note the rvalue reference { ptr = source.ptr; source.ptr = nullptr; }
unique_ptr<Shape> a(new Triangle); unique_ptr<Shape> b(a); // error unique_ptr<Shape> c(make_triangle()); // okay
unique_ptr& operator=(unique_ptr&& source) // note the rvalue reference { if (this != &source) // beware of self-assignment { delete ptr; // release the old resource ptr = source.ptr; // acquire the new resource source.ptr = nullptr; } return *this; } };
unique_ptr& operator=(unique_ptr source) // note the missing reference { std::swap(ptr, source.ptr); return *this; } };
unique_ptr<Shape> a(new Triangle); unique_ptr<Shape> b(a); // still an error unique_ptr<Shape> c(std::move(a)); // okay
expressions / \ / \ / \ glvalues rvalues / \ / \ / \ / \ / \ / \ lvalues xvalues prvalues
unique_ptr<Shape> make_triangle() { return unique_ptr<Shape>(new Triangle); } \-----------------------------/ | | temporary is moved into c | v unique_ptr<Shape> c(make_triangle());
unique_ptr<Shape> make_square() { unique_ptr<Shape> result(new Square); return result; // note the missing std::move }
unique_ptr<Shape>&& flawed_attempt() // DO NOT DO THIS! { unique_ptr<Shape> very_bad_idea(new Square); return std::move(very_bad_idea); // WRONG! }
class Foo { unique_ptr<Shape> member; public: Foo(unique_ptr<Shape>&& parameter) : member(parameter) // error {} };
class Foo { unique_ptr<Shape> member; public: Foo(unique_ptr<Shape>&& parameter) : member(std::move(parameter)) // note the std::move {} };
X::X(const X&); // copy constructor X& X::operator=(const X&); // copy assignment operator X::~X(); // destructor
X::X(X&&); // move constructor X& X::operator=(X&&); // move assignment operator
X& X::operator=(X source) // unified assignment operator { swap(source); // see my first answer for an explanation return *this; }
template<typename T> void foo(T&&);
foo(make_triangle()); // T is unique_ptr<Shape>, T&& is unique_ptr<Shape>&& unique_ptr<Shape> a(new Triangle); foo(a); // T is unique_ptr<Shape>&, T&& is unique_ptr<Shape>&
#include <type_traits> template<typename T> typename std::enable_if<std::is_rvalue_reference<T&&>::value, void>::type foo(T&&);
template<typename T> typename std::remove_reference<T>::type&& move(T&& t) { return static_cast<typename std::remove_reference<T>::type&&>(t); }
80
Matrix multiply(const Matrix &a, const Matrix &b);
Matrix r = multiply(a, b);