Linux系统编程:左右值引用、万能引用、完美转发

本文深入探讨了C++中的右值引用,包括其与左值的区别,以及如何在不可拷贝和可拷贝对象中使用移动构造函数和移动赋值运算符。同时,介绍了万能引用的概念,以及完美转发如何保持参数的原始属性。通过实例代码展示了右值引用在实际编程中的应用。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

1. 左值引用、右值引用

右值: 只能在 = 右边使用的值
字面量、中间结果、临时对象/匿名对象
无法取地址,不能使用左值引用

左值: 可以在 = 左边使用的值

编程,观察现象:

#include <iostream>
using namespace std;

void Print(int n){
        cout << n << endl;
}

void Print2(int& n){            // 左值引用
        cout << n << endl;
}

void Print3(const int& n){      // 解决左值引用问题
        cout << n << endl;
}

void Print4(int&& n){       // 右值引用
        cout << n << endl;
}

int main(){
        int n = 10;
        int m = n;
        // 10 = n;     // 10为右值,字面量

        const char* s = "123edf";
        // "abcd" = s;      // "abcd"为右值,字面量

        m = n + 2;
        // n + 2 = m;       // n + 2 为右值,中间结果

        string str = string(s);
        // string(s) = str;          // string(s)为右值,临时对象

        Print(m);     // int n = m;
        Print(10);     // int n = 10;

        Print2(m);     // int& n = m;
        // Print2(10);     // int& n = 10;    // 字面量(右值)不能初始化左值引用

        Print3(m);     // const int n = m;
        Print3(10);     // const int n = 10;       // 补丁:const左值引用可以初始化左值和右值

        // Print4(m);     // int&& n = m;   // 左值不能初始化右值引用  
        Print4(10);     // int&& n = 10;   
}

结果为:

12
10
12
12
10
10
  1. 10为字面量,为右值
  2. “abcd”为字面量,为右值
  3. n+2为中间结果,为右值
  4. string(s)为临时对象,为右值
  5. 普通函数传值,左值右值都可以传
  6. 10为右值,不能初始化左值引用
  7. 加const,右值也可以初始化左值引用,即左右都可
  8. m为左值,不能初始化右值引用

2. 移动构造函数、移动赋值运算符重载

2.1 不可拷贝对象的移动

对于不可拷贝对象,可以使用移动语法做拷贝构造和赋值。

移动的条件:

  1. 只有右值才能移动,如果左值要移动需要转换成右值move( ) ,或者使用匿名对象 Uncopy()
  2. 对象的类要实现移动构造函数和移动赋值运算符重载
#include <iostream>
using namespace std;

class Uncopy{
public:
        Uncopy() = default;      // 构造函数,不做其他事
        ~Uncopy() = default;       // 析构函数

        Uncopy(const Uncopy&) = delete;              // 拷贝构造函数,不能使用
        Uncopy& operator=(const Uncopy&) = delete;      // 赋值运算符重载

        Uncopy(Uncopy&&){cout << "rvalue copy constructor" << endl;}                  // 移动构造
        Uncopy& operator=(Uncopy&&){cout << "rvalue assign" << endl; return *this;}          // 移动运算符重载
};

void Param(Uncopy w){}

Uncopy Return(){
        Uncopy v;
        // return v;
        return move(v);
}

// 移动条件:只有右值能移动
// 左值变右值:move( )
int main(){
        Uncopy u;
        // Uncopy w = u;       // 拷贝构造
        Uncopy w = Uncopy();     // 移动构造
        Uncopy w2 = move(u);     // 移动构造

        Uncopy v;
        // v = u;          // 赋值运算符重载
        v = Uncopy();           // 移动运算符重载
        v = move(u);            // 移动运算符重载

        // Param(u);       // Uncopy w = u; 拷贝构造
        Param(Uncopy());       // Uncopy w = u;
        Param(move(u));       // Uncopy w = u;

        Return();
}

结果为:

[root@foundation1 C++7.17]# g++ right_value2.cpp -fno-elide-constructors
[root@foundation1 C++7.17]# ./a.out
rvalue copy constructor
rvalue copy constructor
rvalue assign
rvalue assign
rvalue copy constructor
rvalue copy constructor
rvalue copy constructor

2.2 可拷贝对象的移动

对于可拷贝对象,右值的移动不同于普通的赋值,右值的移动会让右值消失

移动的条件:

  1. 只有右值才能移动,如果左值要移动需要转换成右值(move())。
  2. 对象的类实现了移动构造函数和移动赋值运算符重载 。如果没有,执行拷贝操作。
#include <iostream>
#include <vector>
using namespace std;

class Simple{
public:
        Simple(){cout << "constructor" << endl;}
        Simple(const Simple&){cout << "copy constructor" << endl;}          // 加const,左右值都能放
        Simple& operator=(const Simple&){cout << "assign override" << endl; return *this;}

        // 定义移动拷贝构造函数、移动赋值运算符重载
        Simple(Simple&&){cout << "move constructor" << endl;}
        Simple& operator=(Simple&&){cout << "move override" << endl; return *this;}
};

int main(){
        string s = "abcd";
        // string t = s;         // 拷贝构造,数据还在
        string t = move(s);      // 移动后s的数据会消失
        cout << (void*)s.c_str() << " " << s << endl;
        cout << (void*)t.c_str() << " " << t << endl;

        vector<int> vec1 = {1,2,3,4,5};
        vector<int> vec2;
        // vec2 = vec1;           // 赋值运算符重载,数据还在
        vec2 = move(vec1);        // 移动后vec1数据会消失
        cout << vec1.size() << "," << vec2.size() << endl;

        // Simple a;
        // Simple b = a;
        // b = a;

        // 在移动条件下,如果类内未定义移动函数,使用拷贝函数;定义了就使用移动函数
        Simple a;
        Simple b = move(a);
        b = move(a);
}

结果为:

[root@foundation1 C++7.17]# g++ move.cpp -fno-elide-constructors
[root@foundation1 C++7.17]# ./a.out
0x7ffcc17343b0 
0x7ffcc1734390 abcd
0,5
constructor
move constructor
move override

对于vector的移动:需要

        vector<Simple> vs;
        // vs.push_back(a);           // 拷贝
        vs.emplace_back(move(a));       // 移动

调用到移动构造函数

move constructor

vector动态内存是如何增长的?拷贝是如何完成的?
在这里插入图片描述

3. 万能引用、完美转发

万能引用: 用于模板参数
传入左值 T&& => T&
传入右值 T&& => T&&
但左右值传过来都是左值

完美转发:
forward<T>(param) 可以使param数据保持原来属性
该是左值就是左值,该是右值就是右值

#include <iostream>
#include <vector>
using namespace std;

class Simple{
public:
        Simple(){cout << "constructor" << endl;}
        Simple(const Simple&){cout << "copy constructor" << endl;}          // 加const,左右值都能放
        Simple& operator=(const Simple&){cout << "assign override" << endl; return *this;}

        // 定义移动拷贝构造函数、移动赋值运算符重载
        Simple(Simple&&){cout << "move constructor" << endl;}
        Simple& operator=(Simple&&){cout << "move override" << endl; return *this;}

        void Test() /*const*/ &{
                cout << "lvalue Test" << endl;
        }
        void Test() &&{
                cout << "rvalue Test" << endl;
        }
};

// void Func(Simple s){}       // 传值
void Func(Simple& s){cout << "lvalue" << endl;}     // 传左值
void Func(Simple&& s){cout << "rvalue" << endl;}     // 传右值
// void Func(const Simple& s){}     // 传引用

// 模板
template<typename T>
void TemplateFunc(T&& param){      // 通用引用/万能引用
        // Func(param);            //左右值传过来都是左值

        Func(forward<T>(param));     // 完美转发,保持原来左值右值的属性
}

int main(){
        Simple s;
        Func(s);
        Func(move(s));

        // 匿名对象
        s.Test();
        Simple().Test();

        // 模板
        TemplateFunc(s);
        TemplateFunc(Simple());
}

结果为:

constructor
lvalue
rvalue
lvalue Test
constructor
rvalue Test
lvalue
constructor
rvalue
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值