Without Move Semantics (C++03)
- unnecessary copies
// Vector.hpp
template <typename T>
class Vector {
public:
...
void push_back(const T& elem);
...
void push_back(T&& elem);
};
#include <utility> // std::move
std::vector<std::string> coll;
std::string s = getData();
...
coll.push_back(s); // void push_back(const T& elem) is called
coll.push_back(getData()); // temporary object, void push_back(T&& elem) is called
coll.push_back(s + s); // temporary object, void push_back(T&& elem) is called
coll.push_back(std::move(s)); // void push_back(T&& elem) is called
...
The source code of std::move
template <class _Tp>
_LIBCPP_NODISCARD_EXT inline _LIBCPP_HIDE_FROM_ABI _LIBCPP_CONSTEXPR __libcpp_remove_reference_t<_Tp>&&
move(_LIBCPP_LIFETIMEBOUND _Tp&& __t) _NOEXCEPT {
typedef _LIBCPP_NODEBUG __libcpp_remove_reference_t<_Tp> _Up;
return static_cast<_Up&&>(__t);
}
template<class T>
constexpr remove_reference_t<T>&& move(T&& t) noexcept {
return static_cast<remove_reference_t<T>&&> t;
}
std::move
does not move anything, it just cast the parameter into a r-value, so that it can be bound to a r-value reference.
Class
- declare virtual destructor disable move semantics for all the members for this class
The use of a r-value reference is a l-value
#include <iostream>
void g(int&& x) {
std::cout << x << "\n";
}
void f(int&& x) {
g(x);
}
int main() {
f(52);
}
<source>:8:7: error: cannot bind rvalue reference of type 'int&&' to lvalue of type 'int'
8 | g(x);
| ^
<source>:3:14: note: initializing argument 1 of 'void g(int&&)'
3 | void g(int&& x) {
| ~~~~~~^
Universal/forwarding Reference
因為 The use of a r-value reference is a l-value,所以
void call_foo(C&& x) {
foo(std::move(x));
}
必須明確使用 std::move(x)
才能保持 move semantics。
也因此,我們使用 template 結合 forwarding 來自動達成這件事,因為當參數越來越多時,要手動對這些參數 call move 會很麻煩。
#include <utility>
class C {};
void foo(const C& x) {};
void foo(C& x) {};
void foo(C&& x) {};
// void call_foo(const C& x) {
// foo(x);
// }
// void call_foo(C& x) {
// foo(x);
// }
// void call_foo(C&& x) {
// foo(std::move(x));
// }
// Perfect Forwarding
template<typename T>
void call_foo(T&& x) {
foo(std::forward<T>(x));
}
int main () {
C c1;
const C c2;
call_foo(c1); // call foo(C&)
call_foo(c2); // call foo(const C&)
call_foo(C{}); // call foo(C&& x)
call_foo(std::move(c1)); // call foo(C&& x)
return 0;
}
這邊的 T&& x
並不是 r-value reference,而是 l-value, r-value reference 都可以對應,在上面的例子當中就是對應到三個不同的 foo
function。
// Perfect Forwarding
template<typename T>
void call_foo(T&& x) {
foo(std::forward<T>(x));
}