右值、移动语义和完美转发

1. lvalue / rvalue

An lvalue is an expression that refers to a memory location and allows us to take the address of that memory location via the & operator. An rvalue is an expression that is not an lvalue.

即,存在于内存且可用&取地址的表达式为右值,否则为左值。在Value categories 详细的解释了如何区分左值和右值。

1.1 lvalue

int i = 42;
i = 43;             // ok, i is an lvalue
int* p = &i;        // ok, i is an lvalue
int& foo();
foo() = 42;         // ok, foo() is an lvalue
int* p1 = &foo();   // ok, foo() is an lvalue

1.2 rvalue

int foobar();
int j = 0;
j = foobar();           // ok, foobar() is an rvalue
int* p2 = &foobar();    // error, cannot take the address of an rvalue
j = 42;                 // ok, 42 is an rvalue

2. lvalue reference / rvalue reference

lvalue reference: T&
rvalue reference: T&&

3. Rvalue reference

右值引用的两大意义

  • move semantics
  • perfect forwarding

4. Universal references VS Rvalue references

4.1 形式

同为 T&&

4.2 Universal references

可左值可右值,和右值引用的形式相同,一般在以下两种情况出现

  • 函数模板: template <typename T> void f(T&&);
  • auto 声明: auto&& var = var1

4.2.1 reference collapsing

通用引用的类型推导规格,即为reference collapsing,如下
- A& & becomes A&
- A& && becomes A&
- A&& & becomes A&
- A&& && becomes A&&

4.3 Rvalue references

只能绑定到右值。除去Universal references就是右值引用了。

5. move semantics

移动语义,主要解决对象资源所有权转移的问题。在C++11之前,由于移动语义的缺失造成的资源转移需要通过构造(复制)/析构来实现是影响C++性能的一个重要原因。

5.1 std::move

std::move无条件将类型转换为右值。其作用是为了强制实现移动语义。

// vs 2017 : move
template<class _Ty>
_NODISCARD constexpr remove_reference_t<_Ty>&& move(_Ty&& _Arg) _NOEXCEPT
{   // forward _Arg as movable
    return (static_cast<remove_reference_t<_Ty>&&>(_Arg));
}

例如强制将左值转换为右值

int a = 32;         // lvalue
f(std::move(a));    // rvalue

5.2 perfect forwarding

问题引入,如何将一个函数的参数原封不动的传给另外一个函数?std::forward根据类型转换为左值或右值,从而实现完美转发。

// vs 2017 : forward
template<class _Ty>
_NODISCARD constexpr _Ty&& forward(remove_reference_t<_Ty>& _Arg) _NOEXCEPT
{   // forward an lvalue as either an lvalue or an rvalue
    return (static_cast<_Ty&&>(_Arg));
}

template<class _Ty>
_NODISCARD constexpr _Ty&& forward(remove_reference_t<_Ty>&& _Arg) _NOEXCEPT
{   // forward an rvalue as an rvalue
    static_assert(!is_lvalue_reference_v<_Ty>, "bad forward call");
    return (static_cast<_Ty&&>(_Arg));
}

通常用在需要将绑定右值的左值转换为右值的地方,比如,函数参数总为左值,但是其可能绑定了一个右值引用。

Named variables and parameters of rvalue reference type are lvalues.

例如:

int&& a = 32;
void func(int&& param) // 函数参数总为左值
{}

上边aparam都是左值,但是他们都是绑定到了右值上。
如果有两个重载函数

void f1(const int&);
void f1(int&&);

当通过f1(a)调用时,都会调用void f1(const int&);我们可以通过使用std::forward来转换

fw(std::forward<decltype(a)>(a));

其更常用在Universal references的函数中来调用其他函数,因为参数都是左值,所以需要用 std::forward将其类型转换为左值或右值;

template <typename T>
void f(T&& param)
{
    f1(param); // always: void f1(const int&)
}

template <typename T>
void f(T&& param)
{
    f1(std::forward<T>(param)); // depend on T
}

references

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值