CppCon笔记--Back to Basics: Move Semantics

左值不一定是lhs的例子

string s{};
s+s = s;

1.move

万能引用,然后转型成去掉ref的type&&

template <typename T>
typename remove_reference<T>::type&& move(T&& arg) noexcept
{
  return static_cast<typename remove_reference<T>::type&&>(arg);
}
// Simple move constructor
A(A&& arg) : member(std::move(arg.member)) // the expression "arg.member" is lvalue
{} 
// Simple move assignment operator
A& operator=(A&& other) {
     member = std::move(other.member);
     return *this;
}

2.如何写移动构造

2.1.如果不涉及直接资源,如raw ptr,直接用default

rule of zero

2.2.直接涉及资源,手写

此时注意,在传入参数时调用函数时是右值(calling scope),但是函数体内w实际是一个左值。

在move资源时,如果不nulltify将忘的右值,其可能会把被转移的资源给释放了(析构函数)。

当然也可以用std::exchange,把被移动的对象赋0。此外,可以加上noexpect,对速度有提升。

实际上move一个int就是复制一个int,所以不需要设为0,因为这类build-in类型并没有移动赋值和移动构造。

3.如何写移动赋值

写移动赋值,需要记得清理被赋值对象的资源。

使用swap虽然elegant,但是会把旧的资源转给w。

不适用于self-move的情况(加个if判断就好),不过self-move就是不合逻辑的操作。

3.1.rule of 4.5

可以使用在raii那集中的方法,将两个赋值函数合并

4.什么情况编译器会自动生成特殊成员函数(构造,赋值...)

其实是一个rule of thumb的延申,如果自定义了一个特殊成员函数(包括default和delete),你就要手写其他4成员函数(如果不需要move也要删除移动构造和移动赋值函数)

5.函数设计

移动并不一定是最有效的方法,那如何高效地返回或输入数据。比如数组不好移动,可以用f(T&)来返回数据。unique_ptr是f(x),但是调用时需要move,用右值来转移所有权到x

6.forwarding reference

forwarding reference: 通过模板template f(T&&)来保留引用类型

引用折叠能帮助推导出(Widget &是模板推导出的,手写一个Widget& &&是不合法的操作)

  • T&& && ->右值引用
  • T& && ->左值引用

两种引用都能被保留。template f(T&&)(万能引用)能让我们传入任何数据,而template f(const T&)会加上一个const,不一定每次都能符合我们的需要。值得注意的是,左值会被推导成T& &&而右值被推导成T &&。

7.perfect forwarding

forwarding reference能对左值和右值以及左值引用和右值引用进行区分,但是还是没有解决一个问题,在进入函数体后,T&& t实际成了一个命名了的左值,这时候用来初始化实际是一个调用了拷贝构造。但是我们也不能直接使用move,我们需要的是一个有条件的move来解决这个问题(只对右值进行move)。

使用std::forward能解决以上问题。

8.Overloading with forwarding reference

const T& 优先级都不高

优先 完全匹配上的非模板函数

const T&& 是const 右值引用,不是万能引用

template <typename T> void f (T&&);
template <typename T> void g (const T&&);
 
s x;
 
f (s ()); // Ok, T = s
f (x);    // Ok, T = s&
 
g (s ()); // Ok, T = s
g (x);    // Error, binding lvalue to rvalue, T = s

https://www.codesynthesis.com/~boris/blog/2012/07/24/const-rvalue-references/

9.移动语义纠错

需要改成forward (t)

T是类模板,这里的T&&是一个右值引用,所以应该使用move

不能对一个变量forward两次,不然在右值的情况下就不合理了,第一次应该改为拷贝

不需要return move(t),让编译器帮你做(N)RVO


如果能不使用命名变量,那么在c++17的标准下会保证执行RVO

不要返回&&,这就是返回一个要被销毁的局部变量的引用-> dangling reference

注意,如果T是左值,万能引用推导出的是T&,所以要remove_reference

猜你喜欢

转载自www.cnblogs.com/linsinan1995/p/13378456.html
今日推荐