T&&
is used to denote an rvalue reference a reference that can only bind to a temporarystring str = "Hello"s;
string&& ref = str;
input_line_10:3:10: error: rvalue reference to type 'basic_string<...>' cannot bind to lvalue of type 'basic_string<...>'
string&& ref = str;
^ ~~~
string&& ref = "Hello"s;
class movable {
int* _some_data;
public:
movable(movable&& x) noexcept : _some_data{x._some_data} // consume x
{ x._some_data = nullptr; } // leave x destructible
//...
};
instrumented f() {
instrumented x;
return x;
}
instrumented y = f();
void g(instrumented x) { }
g(f());
instrumented h(instrumented x) {
return x;
}
instrumented z = h(f());
= default
noexcept
Example:
class example_01 {
int* _data;
public:
explicit example_01(int x) : _data(new int(x)) { }
~example_01() { delete _data; }
example_01(const example_01&) = delete;
example_01& operator=(const example_01&) = delete;
example_01(example_01&& x) noexcept : _data(x._data) { x._data = nullptr; }
example_01& operator=(example_01&& x) noexcept {
delete _data;
_data = x._data;
x._data = nullptr;
return *this;
}
explicit operator int () { return *_data; }
};
class example_02 {
unique_ptr<int> _data;
public:
explicit example_02(int x) : _data(make_unique<int>(x)) { }
// implicit dtor
// implicit deleted copy-ctor and copy-assignment
/*
move-ctor and move-assignment would be provided by default, but declaring
them ensures they are provided and correct.
*/
example_02(example_02&&) noexcept = default;
example_02& operator=(example_02&&) noexcept = default;
explicit operator int () { return *_data; }
};
What is the post condition of:
string s = "Hello World!";
swap(s, s);
cout << s << endl;
std::swap()
is defined as:
template <class T>
void swap(T& a, T& b) {
T tmp = move(a);
a = move(b); // if a and b alias, then b has been moved from
b = move(tmp);
}
example_01& operator=(example_01&& x) noexcept {
delete _data;
_data = x._data;
x._data = nullptr;
return *this;
}
Is this okay?
example_01 e1(42);
swap(e1, e1);
cout << static_cast<int>(e1) << endl;
void output_01(instrumented& out) {
instrumented tmp;
// fill in tmp
out = tmp;
}
instrumented a1;
output_01(a1);
instrumented output_02() {
instrumented tmp;
// fill in tmp
return tmp;
}
instrumented a2 = output_02();
string append(string str, const char* suffix) {
str += suffix;
return str;
}
instrumented append(instrumented str) {
// modify str
return str;
}
auto str = append(instrumented());
class example_03 {
instrumented _member;
public:
explicit example_03(instrumented data) : _member(move(data)) { }
};
example_03 e_03{instrumented()};
std::move()
is a cast to an rvalue reference.
class example_04 {
instrumented* _member;
public:
example_04() : _member(new instrumented()) { }
~example_04() { delete _member; }
example_04(const example_04& x) : _member(new instrumented(*x._member)) { }
example_04(example_04&& x) : _member(x._member) { x._member = nullptr; }
// this assignment handles both move and copy
example_04& operator=(example_04 x) noexcept {
delete _member;
_member = x._member;
x._member = nullptr;
return *this; }
};
example_04 e41;
example_04 e42;
e41 = e42; // copy
e41 = move(e42);
this
is a hidden argument to a member function*this
may be an rvalueclass example_05 {
instrumented _name;
public:
const instrumented& name() const { return _name; }
};
auto name_01 = example_05().name();
class example_06 {
instrumented _name;
public:
const instrumented& name() const& { return _name; }
instrumented name() && { return move(_name); }
};
auto name_02 = example_06().name();
class example_07; // forward declaration
const example_07& function_01(const example_07&); // You know this works
example_07 function_02(example_07); // This works also!
auto v1 = g1(f());
auto tmp2 = f();
auto v2 = g1(tmp2);
auto tmp3 = f();
auto v3 = g1(move(tmp3));