[C++] 右值引用

根据我个人的理解,引用应当是是C++为了实现与C中的指针相同功能的一种封装。右值引用,就是为了完整实现原有C中的“指针功能”。
传统的引用往往就是左值引用,比如:

int gen = 1;
int& ref_gen = gen;
const int& conref_gen = gen;

左值引用(int& ),一般用于需要在函数内成员对象,比如std::swap。
常量左值引用(const int &),一般用于传入一个较大的数据的时候,比如一个巨大的结构体,比如在传入的时候的拷贝工作。

但是,右值引用解决的一种具体的情形是这样的,如果一个临时的变量(传完即销毁)传入其中,那么就不需要再拷贝一次内存进行赋值,只需要把指针指向临时变量的地址即可。这种,用指针非常容易实现,但是如果使用引用的方式就非常难,因此右值引用就可以避免一次拷贝的过程,最直接的影响就是可以加快程序的运行速度。

class Info
{
private:
    char* _data;
public:
    Info(const char* str = 0)
    {
        _data = new char[strlen(str) + 1];
        strcpy(_data, str);
    }
    Info(const Info& cur)
    {
        _data = new char[strlen(cur._data) + 1];
        strcpy(_data, cur._data);
    }
    Info(Info&& cur) noexcept
    {
        _data = cur._data;
        cur._data = nullptr;
    }
};
int main()
{
    Timer CurTimer;
    CurTimer.StartTimer();
    for (int i=0;i<100000;i++)
    {
        Info cur("10000000086");
        Info l(cur);
    }
    CurTimer.StopTimer();
    std::cout << "left: " << CurTimer.GetTimerMilliSec() << std::endl;
    CurTimer.StartTimer();
    for (int i = 0; i < 100000; i++)
    {
        Info cur("10000000086");
        Info r(std::move(cur));
    }
    CurTimer.StopTimer();
    std::cout << "right: " << CurTimer.GetTimerMilliSec() << std::endl;
    return 0;
}

通用引用
当右值引用和模板类型和auto搭配的时候,T&&不一定代表右值引用,也可能是一个左值引用。具体的类型跟初始化的内容有关,初始化是左值引用,那么之后的类型就是左值引用。(以下的程序说明,左值引用和原值的地址,而右值引用的内容是暂时存储到了一个新的地址里面。

template<typename T>
void func(T&& foo) 
{
    std::cout << &foo << std::endl;
}

int main()
{
    int value = 10;
    int& ref = value;
    int&& lef = value * 1;
    func(ref);
    func(lef);
    std::cout << &value << std::endl;
}
/*
output:
000000E3425EF664
000000E3425EF6C4
000000E3425EF664
*/

完美转发
如果将一个参数交给另外一个函数处理,那么那么另一个函数会不知道这个是左值还是右值。int && a并不是一个右值,而是一个绑定了右值对象的左值。因此,需要有一个函数使得传递的函数常数的类型。(以下的程序可以很好的说明参数在传递过程的类型变化。

#include <iostream>

template<typename T>
void typeprint(T& _) 
{
    std::cout << "left value" << std::endl;
}

template<typename T>
void typeprint(T&& _) 
{
    std::cout << "right value" << std::endl;
}

template<typename T>
void testForward(T&& v) 
{
    typeprint(v);
    typeprint(std::forward<T>(v));
    typeprint(std::move(v));
}

int main()
{
    testForward(1);
    std::cout << std::endl;
    int x = 1;
    testForward(x);
}

/*
output:
left value
right value
right value

left value
left value
right value
*/

STL中也使用右值引用作为参数来加速,比如emplace_back,根据cppreference可以看到,当然构造函数也是需要经过处理的,就跟给出的例子一样。

std::move 是将原本的左值临时显式的变为右值,之后原本的左值应当应当被销毁。
std::forward 用于完美转发的函数。

发表评论

电子邮件地址不会被公开。 必填项已用*标注