首页 > 编程语言 >Effective Modern C++(二)完美转发与移动语义

Effective Modern C++(二)完美转发与移动语义

时间:2023-06-04 17:33:47浏览次数:47  
标签:函数 Effective 对象 Modern C++ 转发 拷贝 移动 实参

 

  • 移动语义使编译器有可能用廉价的移动操作来代替昂贵的拷贝操作。正如拷贝构造函数和拷贝赋值操作符给了你控制拷贝语义的权力,移动构造函数和移动赋值操作符也给了你控制移动语义的权力。移动语义也允许创建只可移动(move-only)的类型,例如std::unique_ptrstd::futurestd::thread

 

移动语义的提出是为了解决拷贝的成本太高这一问题。特别是在对一些将亡值或者说右值的拷贝操作上,这些被拷贝的对象属于临时对象,马上就要被析构调或者不再被使用。

在这种情况下,我们完全可以将被拷贝对象在内存中的资源直接移动给新的对象,再将被拷贝对象置空即可。这样做就避免了逐个拷贝内存中资源带来的成本增加问题。

但是也要注意到,这样做的前提是被拷贝对象将要马上消失;或者说不再被访问,使得我们提前将其释放掉也不会产生问题。

为此我们往往用move()函数来将对象转换问右值,以此来提示编译器或程序员,这个对象是可移动的。

 

  • 完美转发使接收任意数量实参的函数模板成为可能,它可以将实参转发到其他的函数,使目标函数接收到的实参与被传递给转发函数的实参保持一致。

光看定义,一定有菜鸡和我一样想问,目标函数实参和转发函数的实参难道会变吗,不是一直一样吗?以此带动产生第二个问题,完美转发的用处在什么地方?

先说第一个问题,当我们想用一个转发函数将对象A转发到目标函数时,对象A首先作为实参初始化了转发函数中的形参,然后再用转发函数中的形参初始化目标函数中的实参。

看到问题所在了吗,就是这个形参。

形参永远是左值,即使它的类型是一个右值引用。

当然,这是很合理的,因为对一个函数中的形参取地址一定是可行的,这也意味着他是个左值。

void func(int &&a){
    cout<<"right value"<<endl;
}

void func(int &a){
    cout<<"left value"<<endl;
}

void fun(int &&a)
{
    func(a);
}

int main()
{
    fun(1);     //left value
    return 0;
}

 而这也就意味着一个很可怕的事实,在函数转发参数的过程中,对象的左值或右值性丢失了,完美转发正是为了解决这一问题提出的,保证目标函数收到一个左右值不变的对象。

而为了解决这个问题,一个很自然的想法就是利用通用引用,因为通用引用形参被传入实参时才确定是左值还是右值。

class Widget {
public:
template<typename T>
void setName(T&& newName) //newName是通用引用
{ name = std::forward<T>(newName); }


};

利用通用引用,我们将一个左右值不变的对象传入到了std::forward<>()函数中,forward再根据T的左右值产生一个相应的对象。

标签:函数,Effective,对象,Modern,C++,转发,拷贝,移动,实参
From: https://www.cnblogs.com/icecreamjn/p/17455928.html

相关文章

  • C++ chrono
    std::ratio表示一个单位时间。template<std::intmax_tNum,std::intmax_tDenom=1>classratio;Num是时间的分子,Denom是时间的分母。std::milli=std::ratio<1,1000>std::centi=std::<1,100>std::deci=std::<1,10>std::chrono::duration......
  • C++ java 启动器
    #include<Windows.h>#include<stdlib.h>#include<string>intWINAPIWinMain(HINSTANCEhInstance,HINSTANCEhPrevInstance,LPSTRlpCmdLine,intnShowCmd){//设置环境变量java_homestd::wstringjava_home=L"D:\\Tools\\jdk\......
  • c++函数调用压栈过程
    c++函数调用,栈内情况如下图所示:首先主函数将被调函数所需参数从右至左压入栈中然后再将主函数地址即返回地址EIP压入栈中再将主函数栈基址EBP压入栈中,此时构造被调函数栈,将当前ESP值mov给EBP,即被调函数栈从此处开始上图ida反汇编代码,可以看到对变量的使用,参数(argc,argv,env......
  • C++面试八股文:指针占用多少个字节?
    某日小二参加XXX科技公司的C++工程师开发岗位4面:面试官:memset、memcpy和strcpy的区别是什么?小二:memset用于将一块内存设置为特定的值,memcpy用于将一块内存从一个位置复制到另一个位置,strcpy用于将一个字符串从一个位置复制到另一个位置。面试官:嗯,不错。那么你知道这三个函......
  • c++ explicit关键字
    explicit关键字用于修饰单参数构造函数(因为无参数构造函数和多参数构造函数总是显式调用,这种情况在构造函数前面加explicit毫无意义)和转换函数,用于禁止隐式类型转换作用是防止编译器在某些情况下自动执行隐式类型转换,以提高代码的明确性和安全性classA{public:A(int......
  • c++进行map数据横向合并
    请帮我写一段代码。nlohmann::json怎么把三个字典合并到一起,c++14标准,并且每个字典的长度不一样,原数据不是json。例如:data1=[{"key1":"a1","key2":1},{"key1":"a2","key2":2}]data2=[{"key3":"b1",},{"key3&......
  • c++ volatile关键字
    volatile是关键字,用来修饰变量作用禁止编译器对变量的优化编译器在优化代码时,会对变量的读取和写入进行优化,例如将变量从内存加载到寄存器中进行操作,以提高执行效率,但是对于volatile修饰的变量,编译器不能进行这些优化,以确保对变量的每次读写操作都是直接从内存中进行的一......
  • c++ 左值和右值以及左值引用和右值引用
    无法令引用重新绑定到另一个对象上,因此引用必须初始化,引用并非对象,只是为一个已经存在的对象所起的另外一个名字因为引用本身不是一个对象,所以不能定义引用的引用inta=10;int&b=a;int&c=b;//此时c不是引用的引用,而是bc都是a的引用判断某个表达式是左值还......
  • 第十届蓝桥杯c++b组国赛题解(还在持续更新中...)
    试题A:平方序列解题思路:直接枚举一遍x的取值,然后按照题目给定的式子算出y,每次取x+y的最小值即可答案为7020代码实现:#include<iostream>#include<algorithm>#include<cmath>usingnamespacestd;#defineintlonglongconstintN=1e4+5;signedmain(){ //记录答案......
  • Effective Modern C++(一)
    通用引用与右值引用我们以T&&的形式声明一个右值引用,但并不是所有形如T&&形式的声明都为右值引用,他还有可能是一个万能引用。事实上,“T&&”有两种不同的意思。第一种,当然是右值引用。这种引用表现得正如你所期待的那样:它们只绑定到右值上,并且它们主要的存在原因就是为了......