• C++ 11 右值与完美转发


    右值与完美转发

    目录

    右值与完美转发

    左右值的理解

    一般左值

    一般右值

    纯右值

    将亡值

    右值引用

    移动构造函数

    完美转发

    abstract

    Demo

    C++ 返回值优化RVO

    Abstract

    How

    Demo


    左右值的理解

    顾名思义:可以放到等号左边的东西叫左值,不可以放到等号左边的东西就叫右值。

    从程序的角度讲:可以取地址并且有名字的东西就是左值,不能取地址的没有名字的东西就是右值。

    int a = b + c; 

    a是左值,a有变量名,也可以取地址,可以放到等号左边, 表达式b+c的返回值是右值,没有名字且不能取地址,&(b+c)不能通过编译,而且也不能放到等号左边。

    一般左值

    • 函数名和变量名
    • 返回左值引用的函数调用
    • 前置自增自减表达式++i、--i
    • 由赋值表达式或赋值运算符连接的表达式(a=b, a += b等)
    • 解引用表达式*p
    • 字符串字面值"abcd"

    一般右值

    纯右值和将亡值都属于右值。

    纯右值

    运算表达式产生的临时变量、不和对象关联的原始字面量、非引用返回的临时变量、lambda表达式等都是纯右值。

    举例:

    • 除字符串字面值外的字面值
    • 返回非引用类型的函数调用
    • 后置自增自减表达式i++、i--
    • 算术表达式(a+b, a*b, a&&b, a==b等)
    • 取地址表达式等(&a)

    将亡值

    将亡值是指C++11新增的和右值引用相关的表达式,通常指将要被移动的对象、T&&函数的返回值、std::move函数的返回值、转换为T&&类型转换函数的返回值,将亡值可以理解为即将要销毁的值,通过“盗取”其它变量内存空间方式获取的值,在确保其它变量不再被使用或者即将被销毁时,可以避免内存空间的释放和分配,延长变量值的生命周期,常用来完成移动构造或者移动赋值的特殊任务。

    右值引用

    int a = 4;
    int &&b = a; // error, a是左值
    int &&c = std::move(a); // ok

    如果使用右值引用,那表达式等号右边的值需要时右值,可以使用std::move函数强制把左值转换为右值。

    移动构造函数

    如果不使用移动构造函数,拷贝本身代表着复制,代表着内存和时间的开销,但是如果我们不需要复制,只是希望这个内存换一个地方继续工作,那就可以使用转移 std::move

    class A {
    public:
        A(int size) : size_(size) {
            data_ = new int[size];
        }
        A(){}
        A(const A& a) {
            size_ = a.size_;
            data_ = new int[size_];
            cout << "copy " << endl;
        }
        A(A&& a) {
            this->data_ = a.data_;
            a.data_ = nullptr;
            cout << "move " << endl;
        }
        ~A() {
            if (data_ != nullptr) {
                delete[] data_;
            }
        }
        int *data_;
        int size_;
    };
    int main() {
        A a(10);
        A b = a;
        A c = std::move(a); // 调用移动构造函数
        std::vector vecs;
        std::vector vecm = std::move(vecs); // 免去很多拷贝
        return 0;
    }
    
    

    完美转发

    abstract

    完美转发指可以写一个接受任意实参的函数模板,并转发到其它函数,目标函数会收到与转发函数完全相同的实参,转发函数实参是左值那目标函数实参也是左值,转发函数实参是右值那目标函数实参也是右值。那如何实现完美转发呢,答案是使用std::forward()。

    Demo

    void PrintV(int &t) {
        cout << "lvalue" << endl;
    }
    
    void PrintV(int &&t) {
        cout << "rvalue" << endl;
    }
    
    template
    void Test(T &&t) {
        PrintV(t);
        PrintV(std::forward(t));
    
        PrintV(std::move(t));
    }
    
    int main() {
        Test(1); // lvalue rvalue rvalue
        int a = 1;
        Test(a); // lvalue lvalue rvalue
        Test(std::forward(a)); // lvalue rvalue rvalue
        Test(std::forward(a)); // lvalue lvalue rvalue
        Test(std::forward(a)); // lvalue rvalue rvalue
        return 0;
    }

    C++ 返回值优化RVO

    Abstract

    返回值优化(RVO)是一种C++编译优化技术,当函数需要返回一个对象实例时候,就会创建一个临时对象并通过复制构造函数将目标对象复制到临时对象,这里有复制构造函数和析构函数会被多余的调用到,有代价,而通过返回值优化,C++标准允许省略调用这些复制构造函数。

    How

    • return的值类型与函数的返回值类型相同
    • return的是一个局部对象

    Demo

    std::vector return_vector(void)
    {
        std::vector tmp {1,2,3,4,5};
        return tmp;
    }
    
    std::vector rval_ref = return_vector();
    //这段代码会触发RVO,不拷贝也不移动,不生成临时对象。
  • 相关阅读:
    回溯算法(3)--n皇后问题及回溯法相关习题
    Android 10 如何在SurfaceFlinger中解决开机动画显示不全问题
    详解 Java 19 中的记录类型的模式匹配
    ChatGPT实战与私有化大模型落地
    怎么在Qt中使用AIUI
    nodejs+vue+elementui招生信息网站管理系统
    基于JAVASketch2Mod网站计算机毕业设计源码+系统+lw文档+部署
    windows与Ubuntu实现文件夹共享
    radio日志sim卡信号状态分析
    服务器数据恢复-服务器系统损坏启动蓝屏的数据恢复案例
  • 原文地址:https://blog.csdn.net/qq_32378713/article/details/126242664