右值

右值

每个 C++ 表达式都有一个类型,属于值类别。 值类别是编译器在表达式计算期间创建、复制和移动临时对象时必须遵循的规则的基础。
C++17的值类别有:

  • glvalue (generalized lvalue):表示对象身份的表达式,如变量名、函数名、类名等(包括具名右值引用,匿名右值引用比如std::move(x)是右值)。
  • prvalue (pure rvalue):表示纯右值的表达式,如字面量、临时对象等。没有可供程序访问的地址。包括文本,返回非引用引用的函数调用,只有编译器访问的临时资源。
  • xvalue (expiring value):表示一个对象或位域,该对象或位域的资源可重复使用(通常是因为它接近其生存期的末尾)。有地址,但地址不再供程序访问,但可以用于初始化rvlaue引用。比如返回右值引用的函数调用。
  • lvalue (lvalue):表示一个对象或位域,该对象或位域具有标识符并且可用作左值。具有程序可访问的地址。
  • rvalue (rvalue):prvalue 或 xvalue。

右值包括字面常量和求值过程中创建的临时对象,这些对象将要被销毁且没有其他用户。
函数参数都是左值。

右值引用

绑定到右值的引用。只能绑定到将要销毁的对象,因此可以将右值引用的资源移动到另一个对象中。
右值引用可以延长临时对象的生命周期(常量左值引用也可以),直到右值引用结束。

move

利用移动语义,可以编写将资源(如动态分配的内存)从一个对象转移到另一个对象的代码,允许从临时对象(无法在程序中的其他位置引用)转移资源。
move用于获得绑定到左值的右值引用,告诉编译器希望像右值一样处理左值,并承诺不再使用该左值(可以销毁移后对象,也可以赋予新值,但是不能使用移后源对象的值)。
使用时应该直接调用std::move而不是move,避免潜在名字冲突

引用折叠

参数推断:将左值传递给右值引用参数T&&时,T被推导为左值引用类型。
创建引用的引用形成了折叠

  • T& &、T& &&、T&& & 都折叠为T&
  • T&& &&折叠为T&&

结合上面两条

template void f(T&&)

对该函数传递左值,会实例化出

void f<int&>(int&)

所以实际上我们可以把任何类型的参数传递给T&&类型的函数。
需要注意的是,传递左值引用时,函数内部可能造成错误的效果,因此使用下面的方式进行重载。
以下两个模板能适用于所有类型的参数,但是对不同类型的匹配程度不同,因此会优先匹配更精确的模板。

template void f(T&&) // 非const右值,右值精确匹配,可以窃取资源
template void f(const T&) //左值和const右值

move源码

template <typename T>
typename remove_reference<T>::type&& move(T&& param)
{
    return static_cast<typename remove_reference<T>::type&&>(param);
}

根据以下例子进行分析

string s1("hi"), s2;
s2 = move(string("bye"));
s2 = move(s1);

第一个赋值

  • T为string
  • param为string&&
  • remove_reference::type为string
  • 返回类型为string&&

因此实例化为

string&& move(string&& param)

static_cast转换什么都不做

第二个赋值

  • T为string&
  • param为string&
  • remove_reference::type为string
  • 返回类型为string&&

因此实例化为

string&& move(string& param)

使用static_cast将左值转换为右值引用是允许的。

forward

函数参数为T&&时,无论传递的实参是左值还是右值,函数内部该参数都是左值,导致内部调用其他函数时右值引用的参数无法接受左值。
某些函数将一个或多个实参联通类型不变地转发给其他函数,需要保持实参的所有性质,包括是否为const以及左值或右值。
forward使用显式模板实参来调用,返回显式实参的右值引用。即forward返回类型为T&&。

template <typename T>
intermediary(T&& param)
{
    finalFcn(forward<T>(param));
}

实参为右值时,T被推导为非引用类型,因此forward返回类型为T&&。
实参为左值时,T被推导为左值引用类型,因此forward返回类型为T& &&,根据引用折叠规则,类型为T&。
从现象上看和'static_cast<T&&> (param)'效果一样。

posted @   trashwin  阅读(127)  评论(0编辑  收藏  举报
相关博文:
阅读排行:
· 全程不用写代码,我用AI程序员写了一个飞机大战
· DeepSeek 开源周回顾「GitHub 热点速览」
· MongoDB 8.0这个新功能碉堡了,比商业数据库还牛
· 记一次.NET内存居高不下排查解决与启示
· 白话解读 Dapr 1.15:你的「微服务管家」又秀新绝活了
点击右上角即可分享
微信分享提示