在template的声明式中,class和typename没区别
但如果要在模板参数内部,要typename定义嵌套从属类型的时候 就要typename指定从属类型
比如模板函数里面还有一个类型比如 T::const_iterator* x;如果没用typename的话就可能会被编译器识别为静态成员,就会有二义性,为了让这个T::const_iterator成为T中的一个类型,而不是静态成员就要直接在前面加个typename:
template<typename T>
void Func(const T& arg)
{
typename T:: const_iterator* x;
}
编译器就能知道这个是一个类型
下面开始C++11标准
右值引用
左值和右值,左值引用和右值引用是C++11中重要概念,是理解move/forward等新语义的基础
左值:
既能出现在赋值符号“=”左边也能出现在右边的变量或表达式
右值:
只能出现在赋值符“=”右边的的变量(或表达式)
判断标准:
一般,有名字的变量就是左值,而由运算符操作所产生的中间结果(没有名字)就是右值,左值就是程序中能寻址的东西,右值就是没办法寻址的东西
在C++11中所有的值必属于左值、纯右值、将亡值之一。比如临时变量、(函数返回值的临时变量、运算表达式产生的临时变量)原始字面量等都是纯右值。而将亡值是C++11与右值引用相关的表达式(将要被移动的对象、std::move返回值等)
什么是左值引用
引用就是某一变量的别名,对引用的操作与对变量直接操作完全一样
对左值的引用叫做左值引用,就是之前学过的引用(变量别名),为了和新标椎中引入的“右值引用”相区别,所以现在叫左值引用
type& name = 左值表达式;
什么是右值引用
对右值(不能取值)的引用叫右值引用
type && name = 右值表达式;
int && a1=0;
int && a2 = GetVal();
int val = 100;
int && a3 = val;//? 不成立 但可以用std::move转化
void func(int && val)
{
std::cout<<val<<std::endl;
}
右值引用独立于右值和左值,虽然是对右值的引用,但他本身还是个左值 比如a1 a2就是左值
func(a1); 会直接报错 但可以传GetVal()
意义
右值引用绑定了右值,让临时右值的声明周期延长了(续命),可以利用这个特点避免临时对象的拷贝构造和析构,优化性能
常量左值引用也可以性能优化。输出结构和右值引用相同,因为常量左值引用是一个“万能”的引用类型,可以接受左值、右值、常量左值和常量右值
const int &res = GetVal();//也没拷贝了
在C++11以前右值能被const类型的引用所指向
通用引用
定义函数形参的时候,可以指定:
右值引用:
void f(int && arg);//右值引用,不能传左值
左值引用:
void f(int &arg);//左值引用,不能传右值
通用引用:
void f(T && arg);//通用引用,类型为模板参数
如果被一个左值初始化,他就是左值,如果被一个右值初始化,他就是一个右值,取决于它的初始化。
template<typename T>
void f(T &&arg);
int i = 10;
f(i); //左值
f(10); //右值
引用折叠规则
模板函数:
template<typename T>
void f (T&& arg);//通用引用,如果T本身也是引用 比如 T&,或者T&&,那么原函数就会展开为:T& &&和 T&& &&,
void f2(T & arg);T本身就是 T& ,T&& 那么展开为 T& &,和T&& &。
引用折叠规则:
x& &,x& &&,x&& &折叠为:x&(单个和单个,单个和双个)
x&& && 折叠为 X&&(双个和双个)
引用折叠解释通用引用:
int i = 10;
f(i); i: int & -> int & &&->int &
f(10); 10 : int -> int&&
完美转发
完美转发: 在函数模板中,完全依照模板的参数类型(左值,右值特征),将参数传给函数模板中调用的另外一个函数。
void process(int&t){cout<<“lvalue”;}
void process(int&&t){cout<<"rvalue";}
template<typename T>
void Test(T&& v)//是Universal引用,既可以是左值,也可以是右值,左值转发给左值函数,右值转发给右值函数
{
//转发1:不完美转发
process(v);//v具有变量,本身是左值,调用process(int &t)
//转发2: 完美转发
process(std::forward<T>(v));//按v被初始化时的类型转发(左值或右值)
//转发3:强制将V转为右值
process(std::move(v));//将v强制转为右值,调用process(int&& t)
}