T&& 右值引用

本文探讨了C++中的左值与右值概念,重点讲解了右值引用如何延长临时对象生命周期,避免深拷贝,以及std::forward在完美转发中的应用。通过实例演示了如何在MyString类中利用右值引用提高效率,并介绍了如何在模板函数中实现参数的正确转发。
摘要由CSDN通过智能技术生成

左值与右值
左值:表达式结束后仍存在的持久对象;
右值:表达式届时后就不再存在的临时对象。包括将亡值和纯右值。
将亡值:T&&函数返回值,std::move返回值,将要被移动的对象等。
纯右值:非引用返回的临时变量,运算表达式产生的临时变量,原始字面量和lambda表达式等

右值引用的作用
让被引用的右值“重获新生”,其生命周期和右值引用类型变量的生命周期一样,不会被马上析构掉。

struct A
{
    A() { cout << "construct: " << ++constructCount << endl; }

    A(const A& another) { cout << "copyConstruct: " << ++copyConstructCount << endl; }

    ~A() { cout << "destruct: " << ++destructCount << endl; }
};

A getA()
{
    return A();
}

int main()
{
    A& a = getA();
    A&& b = getA();

    return 0;
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
如果是a的话会调用临时变量到a的拷贝构造函数,随后临时变量被析构;
但是b的话不会调用临时变量到b的拷贝构造函数!!!相当于b直接用临时变量的内存和数据,节约资源!!!

含有堆内存的class,拷贝构造以及拷贝赋值函数,需要深拷贝。而使用右值引用的拷贝构造函数和拷贝赋值函数,可以避免对临时对象的深拷贝,直接使用临时对象已经申请的资源,节省资源和时间。

class MyString
{
private:
    char* m_data;
    size_t m_len;

    void CopyData(const char* s)     //深拷贝数据
    {
        m_data = new char[m_len + 1];
        memcpy(m_data, s, m_len);
        m_data[m_len] = '\0';
    }

public:
    MyString() : m_data(nullptr), m_len(0) {}

    MyString(const char* s)
    {
        m_len = strlen(s);
        CopyData(s);
    }

    MyString(const MyString& another) : m_len(another.m_len)
    {
        CopyData(another.m_data);
    }

    MyString& operator=(const MyString& another)
    {
        this->m_len = another.m_len;
        CopyData(another.m_data);

        return *this;
    }

    ~MyString() { if (m_len) delete[] m_data; }
};

int main()
{
    MyString a;
    a = MyString("Hello");   // 调用拷贝赋值函数,有对临时对象“Hello”深拷贝
    std::vector<MyString> vec;
    vec.push_back(MyString("World")); // 调用拷贝构造函数,有对临时对象“World”的深拷贝

    return 0;
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
MyString(“Hello”)和MyString(“World”)都是临时对象,即右值,但程序仍然会调用拷贝构造和拷贝赋值函数,造成没意义的资源申请和释放,而通过设计右值引用的构造函数,则可以避免浪费,尤其是对需要动态申请大量资源的类。

MyString(MyString&& another)
    {
        m_len = another.m_len;
        m_data = another.m_data;       //此处是浅拷贝即可
        another.m_len = 0;
        another.m_data = nullptr;
    }
 

完美转发
首先运行下示程序:

template<typename T>
void PrintT(T& t){
    cout << "lvalue" << endl;
}
 
template<typename T>
void PrintT(T&& t){
    cout << "rvalue" << endl;
}
 
template<typename T>
void TestForward(T&& v){
    PrintT(v);
}
 
void main(){
    TestForward(1);
    int x = 1;
    TestForward(x);
}
运行结果:

 

 x是左值所以很好理解输出结果为lvalue。但1作为右值输出为lvalue是为什么?

因为1是右值,所以未定义的引用类型T&& v被一个右值初始化后变为具名的右值引用,所以在TestForWard函数内调用PrintT(v)时,此时v是具名变量,它是左值,所以被PrintT(T& )所调用,从而结果打印lvalue。(参照之前在右值引用处总结的三、四条)

那么我们想要保证参数按照原来类型转发到其他函数,就上例而言,希望v依然作为右值被转发。这种转发被称为完美转发。

实现就是靠std::forward函数实现:

template<typename T>
void TestForward(T&& v){
	//PrintT(v);
	PrintT(std::forward<T>(v));  // forward实现完美转发
}

 

 

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值