1.decltype
decltype可以推导出一个表达式的类型,可以用这个类型定义变量。
测试代码
template<class T1,class T2>
void test1(T1 &a,T2 & b)
{decltype(a* b) ret;cout << typeid(ret).name() << endl;
}
2.范围for
void test2()
{string s("hello world");for (char a : s){cout << a <<" " ;}
}
可以将,s的内容放到a变量中,然后自动遍历迭代。
3.auto
auto可以自动推断出类型,主要是解决迭代器的名字太长的原因。
这是string迭代器的类型,而且string这个类型名字还是比较短的。
有了auto就可以这么写,是不是感觉很爽。
void test3()
{string s;auto it = s.begin();cout << typeid(it).name() << endl;
}
auto还可以做函数的返回值
auto test4()
{string s("hello world");return s;
}
但是不建议这么使用,一但出现函数调用函数,想看这个函数的返回值都很费劲。
auto test5()
{return 0;
}auto test6()
{return test5();
}
auto test7()
{return test6();
}
auto test8()
{return test7();
}
如果我这样套起来,本来我想看看test8返回什么,这时候就得一直向上找,最后找到test5才知道test8返回的是一个int。
4.左值引用和右值引用
先说结论
C++11新加的右值引用有什么意义,之前只有左值引用,我们传参和函数返回值的时候,可以用引用减少拷贝,提高效率,但是函数引用做返回值的如果是一个临时的变量,返回的时候,该对象会被销毁,引用返回就没有用了,但是右值引用可以间接的解决这个问题,进而提高效率。
这个test返回的是一个custorm类,函数返回都会创建一个临时变量,返回这个临时的变量。
左值引用还是右值引用都是给变量起别名。
我们之前学习的引用,都是左值引用。
1.什么是左值
左值是一个具有长久生命周期,可以被赋值,可以被修改,取地址的表达式。
以下都是左值
int a = 0;
int *pa = &a;
int arr[10] = {0};
2.什么是右值
右值是一个将亡值,快要死了生命周期很短,不能被修改,不能被取地址,一般都是临时对象,表达式的结果,函数返回值,字面常量。
5; //5字面常量就是右值int x 0 ,y = 1;
x + y; //结果就是右值
fun1();//返回值是右值
3.右值引用的意义
对右值的引用,就是给右值取别名。
1.左值引用解决了,传参的时候拷贝的问题。
2.左值引用解决了,函数返回非局部对象拷贝的问题。
3.但是左值引用并不能解决,局部对象返回拷贝的问题,局部对象出了函数作用域销毁,右值引用就是解决局部对象返回拷贝的问题。
4.返回局部对象的成本
示例代码
#include<iostream>
#include<string>class custorm
{
public:custorm(){}custorm(const custorm& s){std::cout << "custorm(const custorm& s)" << std::endl;_number = s._number;_name = s._name;_adress = s._adress;}const custorm& operator=(custorm s){std::cout << "const custorm & operator=(custorm s)" << std::endl;std::swap(_number, s._number);std::swap(_name, s._name);std::swap(_adress, s._adress);return *this;}private:int _number = 0;std::string _name = std::string();std::string _adress = std::string();
};custorm transaction()
{custorm a;std::cout << "buy"<< std::endl;return a;
}int main()
{custorm ret;ret = transaction();return 0;
}
编译器不优化返回需要付出的成本
一次构造和一次拷贝构造。
但是别忘了,custorm对象里有两个string,每个string还需要一次构造,和一次拷贝构造。
编译器优化返回需要付出的成本
不错只需要一次构造,但是还不够。
5.右值引用如何解决局部对象返回拷贝的问题
我们通过将拷贝构造重载一个右值引用的版本,将函数的返回值强行识别为右值,这样经过编译器优化,直接会调用右值引用的版本拷贝构造,构造ret,因为是右值,是将亡值,所以我们可以肆无忌惮的转移其资源,我们可以直接讲将亡值的资源,转移给要构造的对象,把要构造对象的资源转移给将亡值,这样将亡值死后还会把没用的资源释放,直接榨干其最大的价值。
template<class T>void swap(T &t){_number = t._number;_name = t._name;_adress = t._adress;}custorm(custorm&& s){std::cout << "custorm(custorm&& s)" << std::endl;swap(s);}
直接转移资源,一次拷贝都没有,效率大大提高。
6.move
为什返回的时候会直接调用右值引用版本的拷贝构造呢?
因为ret返回的时候,被编译器自动使用move(),将ret变成右值,会调用更适合自己的构造函数。
返回一个对arg的右值引用,这个帮手函数强制移动值的语义,即使值是一个变量:直接使用返回值,会使arg被认为是一个右值。
7.右值引用右值,右值的属性变为左值
之前右值的定义,右值一般都是不可以修改的,但是我们在拷贝构造的时候,却肆无忌惮的,转移资源,这就是因为右值引用右值,右值的属性变为左值,不然我们也不可能转移资源。