1.auto
1.1 auto自动推导
auto能自动推导变量的类型,但使用auto自动类型推导必须给变量初始化,不然无法推导。auto可以与指针、引用结合,也可以与const、volatile限定符配合使用。只有当变量是指针或者引用类型时,推导结果才会保留const,volatile关键字。
int animal = 1024;
auto cat = animal;//cat推导为int类型, auto对应int
auto dog;//dog未被初始化, 不能被自动推导 **语法错误**
auto* pet = &animal;//pet推导为int*, auto对应int
const auto pig = animal;//pig为const int类型,auto对应int
auto duck = pig;//duck为int类型 **pig是const int,但duck没有指针或引用,所以不保留const**
const auto& horse = animal;//horse为const int类型
auto& mouse = animal;//mouse为int&类型
auto一般适用于替代迭代器的定义和泛型编程中。
- 如果变量的定义太冗长,可以用auto替代
vector<int>val{ 1,2,3 };
auto it = val.begin();//vector<int>::iterator it,简化了很多
for (;it != val.end();it++)
cout << *it;
- 在泛型编程中,如果要接收一个模板变量,是很难确定变量的类型,所以用什么数据类型接收就是一件非常头疼的事。解决这个问题,不仅要在模板声明中添加多个变量,在模板函数调用中还要多写多个参数。但如果有了auto关键字,就迎刃而解了。只需将不确定类型的变量用auto接收即可完成任务。
template <class T,class P>
T test1(T val) {
P res = val;//不仅要添一个class P,调用时也要多传一个参数。
return res;
}
template <class T>
T test2(T val) {
auto res = val;//auto 定义res省去了很多工程
return res;
}
int main() {
cout << test1<int,int>(2);
cout << test2<int>(2);
}
1.2 auto的限制
- 不能在函数参数中使用
int test(auto a){ //*错误*
return -1;
}
- 不能用于初始化类的非静态成员变量
又因为静态非常量成员不能在类内初始化,所以在类内auto只能修饰静态常量成员
class test {
public:
auto a = -1; // *错误*
static auto b = -1; // *错误* 静态非常量要在类外初始化
static const auto c = 'c';
}
- 不能定义数组
int arr[]={1,2,3};
auto suss=arr;//正确,arr1为int*类型
auto err1[]=arr;// *错误*
auto err2[]={1,2,3};// *错误*
- 不能当作模板参数
template <typename T>
T test(T val) {
return val;
}
int main() {
cout << test<int>(2);//正确
cout << test<auto>(2);// *错误*
}
2.decltype
语法格式:decltype(表达式)
在变量类型不确定又不想声明的情况下可以用到decltype。decltype对变量类型的推导是在编译期完成的,只用于表达式类型的推导而不计算表达式的值。
推导规则
-
表达式为变量,推导得到的类型与变量类型一致
-
表达式为函数,推导得出的类型与函数返回值一致
如果const修饰的是纯右值(纯数据)时,const不会保留,比如const int类型会推导成int。
- 左值表达式或者被括号修饰,推导结果为表达式类型的引用,如果有const、volatile是不能省略的。
//括号修饰
const int const_int = 10;
int normal_int = 10;
decltype((const_int)) v1= 3;//const int& v1
decltype((normal_int)) v2= normal_int;//int& v2
//表达式修饰
int m = 1, n = 2;
decltype(m+n) v3 = 3;//int v3 m+n是右值
decltype(n=m+n) v4 = normal_int;//int& v4 左值表达式推导结果为引用
decltype同auto一样,广泛应用于泛型编程中。比如说,写了个类模板,里面用到了容器,想用迭代器遍历容器中的数据,迭代器如何定义?如果用 T::iterator it; 之类的语法是错误的,因为编译器无法识别。最好的方案就是用decltype类型推导。
template <class T>
class Container
{
public:
void func(T& c)
{
for (it = c.begin(); it != c.end(); ++it)
{
cout << *it << " ";
}
cout << endl;
}
private:
//T::iterator it;// *错误* 这里不能确定迭代器类型
decltype(T().begin()) it;
};
总结一下,auto的使用必须初始化,decltype可以不用初始化。
3.融合!返回值类型后置
上述文章阐述了auto和decltype的用法,此处讲解一个两者结合的使用方法。
在设计模板函数或者模板类的时候,难免会出现模板变量类型不一样的情况。加入需要两个数值类型的加和运算的模板函数,返回两者的加和。我们知道数值类型有很多,short,int,float,double,long等等,甚至字符变量也能掺和掺和。不同的组合的返回类型也是不同的,但在设计模板函数时在一开头就要给出返回类型,在没有确定参数类型时给出返回类型似乎是一件不可能的事情,C++14中编译器才可以自己推导任何函数的返回类型,如果C++11的编译器用auto做函数返回类型是错误的,因为没有初始化。所以就想到了decltype,然而decltype的使用需要知道传递的参数,如果这么写
decltype(t+u) add(T t, U u)
肯定是错误的,因为decltype中的t和u还没给出,编译器无法识别直接报错。如果能让返回类型放在参数列表之后定义就好了。所以便有了下述的代码思路
template <class T,class U>
auto add0(T t, U u) -> decltype(t+u)
{
return t + u;
}
这就是返回值类型后置。