您的位置:首页 > 编程语言 > C语言/C++

【C++】C++11特性:右值引用与转发

2016-10-02 23:37 281 查看

右值引用

前C++11版本的赋值操作中,中间临时变量必须拷贝赋值给左值变量,然后临时变量被销毁,这过程中临时变量的构造和销毁占用计算机资源;STL引入swap函数来快速交换两个数据,一般是通过交换指针来避免大量数据的拷贝。如果好好利用临时变量,可以提升的效率相当可观。

右值引用相比普通引用,它可以指向临时变量,并且可以直接将临时变量转移给左值变量。右值引用的符号为&&。右值引用的用途有:

消除两个对象交互时不必要的对象拷贝,节省运算存储资源,提高效率。

完美转发

C++类增加了转移构造函数和转移赋值函数用于将临时变量转移到当前对象,除了变量前缀为&&,编写方式与拷贝构造函数和拷贝赋值函数一样。swap函数引入右值引用,这样交换两个对象就无需构造和销毁中间临时变量。

template<typename T,typename U>
void Swap(T &&a, U &&b){
int tmp(std::move(a));
a=std::move(b);
b=std::move(tmp);
}

int a = 10;
int &&b=15;
Swap(a,b);
Swap(a,5);//a=5
cout << "a:"<<a<<" b:"<<b<<endl;


其中std::move函数是强制左值引用变为右值引用,这样赋值操作符就可以默认调用转移赋值函数。

再看一个例子:

void F(const int& a){cout << "const int& "<<a<<endl;}
void F(int& a){cout << "int& "<<a<<endl;}
void F(int&& a){cout << "int&& "<<a<<endl;}

int a = 10;
const int &b = a;
int &&c = 10;
F(a);//F形参为int &
F(b);//F形参为const int&
F(10);//F形参为int &&
F(c);//F形参为int&
F(std::move(c));//F形参为int &&


值得注意的是最后两个用法,c是具名右值引用,具名右值引用被认为是左值,因此调用的函数是F(int&);具名右值引用可以用std::move强制变为真正的右值引用。

转发

程序猿希望只写一个入口函数,就可以传入任意类型的参数,且保证传入的参数的类型不变。设类型为T,传入函数的参数类型有这几种情况:const T, T, const T&, T&, const T&&, T&&。const T和T是值传递,效率很差;const T&和T&不能传递右值引用。

模板特性typename T,T表示的类型是包含const修饰的。也就是说,模板T可以表示int,也可以表示const int。这样const属性依靠模板特性就被保留了。

为了保证引用和右值引用都能正确传递,C++11引入了引用折叠规则:&和&&的任何组合形式都推导为引用&;只有传入参数类型为&&,函数参数类型为&&,函数体中的参数引用才是右值引用。

现在来看上面的swap函数。该swap函数的函数参数类型为T&&,首先const属性通过模板特性保留下来;如果传入参数是左值引用&,那么swap函数体中该变量仍然是左值引用;如果传入参数是右值引用,那么函数体该变量就是右值引用。

遇到嵌套调用的函数,情况就不一样了。看下面代码:

void F(const int& a){cout << "const int& "<<a<<endl;}
void F(int& a){cout << "int& "<<a<<endl;}
void F(int&& a){cout << "int&& "<<a<<endl;}
template<typename T>
void foo(T&& a){
F(std::forward<T>(a));
F(a);
cout << endl;
}

int a = 10;
const int &b = a;
int &&c = 10;
foo(a);
foo(b);
foo(10);
foo(c);
foo(std::move(c));


输出为:

int& 10

int& 10

const int& 10

const int& 10

int&& 10

int& 10//错误!

int& 10

int& 10

int&& 10

int& 10//错误!

可以看到两个右值引用的嵌套函数调用错了。为什么呢?进入foo函数的时候,右值引用a的调用规则是int&& &&->int&&。然而还有一条规则可导出同样的结果:int &&->int&&。foo函数中模板T=int&&;然而调用F函数时,C++又自动推导了形参a的类型,默认推导为int,即第二条规则,然后就会调用F(int&)函数。这时候程序猿需要使用std::forward函数来明确告诉F函数的形参类型是foo函数的形参模板T,即int&&,最终调用正确的函数F(int&&)。

右值引用在节约资源上是个好东西,不过C++的坑又多起来了:C++组委会你们确定初级中级C++程序猿能写好C++11程序?
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签:  c语言 stl C++