C++11,14新语法1-类型推导

C++中每个表达式都有一个结果类型,在以前,如果我们要保存一个表达式的结果,那么需要声明一个与表达式结果类型相同(或者可以转换)的变量来保存结果值。这样不仅要打很多字(想象下std::vector<int>::iterator...),而且还会引起类型不安全的问题。

在C++11中,规范提供了多种类型推导的机制,使得我们写出来的代码更精简、更灵活。

这个新的类型推导机制,主要由两个新的关键字表示:auto和decltype。虽然它们之间有些重叠,但使用场景是不同的。auto只能用来声明变量,而decltype则更为通用。

auto 101

在编写C++代码时,你一定写过类似std::map<std::string, std::vector<int>>::iterator这样麻烦的代码。这样长的类型名字不仅写起来很费劲,而且用在类型模板作为类型参数时更容易造成麻烦。用新的auto关键字就可以解决这个问题:

 1 #include <vector>
 2 using namespace std;
 3 
 4 int main(){
 5     vector<int> coll = {1, 2, 3, 4, 5, 6};
 6     int result = 0;
 7 
 8     for (auto itr = coll.cbegin(); itr != coll.cend(); ++itr)
 9         result += *itr;
10 
11     return result;
12 }

在这个例子中,我们使用auto关键字来指示编译器推导出for的循环变量itr的类型std::vector<int>::const_iterator,如果以后程序变动了,例如需要一个可修改容器元素的迭代器(std::vector<int>::iterator),那我们只需将cbegin()和cend()改为begin()和end()即可。

其实auto不算是一个新的关键字,因为在之前的c++规范中,它是用作指示一个变量是“本地变量”,但在实际情况下函数内部声明的变量都自动是“本地变量”了,所以auto的这种语义是多余的,C++11规范将其改变了用途。

使用auto声明的变量与其他变量一样,都有一个编译器能确定的具体类型。编译器是通过变量的初始化表达式的类型来推导出auto变量的类型,如果编译器不能推导出auto变量的类型,那么会产生编译期的错误。以下auto的用法是错误的:

1 void invalid(auto i){} //没有制定函数形参类型
2 
3 class A{
4     auto m_; // 没指定成员变量类型
5 };
6 
7 int main(){
8     auto arr[10];// 没制定数组元素类型
9 }

 

看了上面的例子,你可能会认为auto只是一个新的语法糖,只是简少了部分代码量。但实际情况是在某些情况下如果没有auto,那么有些C++11的代码是很难或者根本无法写出来的。例如,某个函数模板内的代码依赖于模板类型参数的实际类型,那么在编写代码时就很难显式指出这个类型。但这个类型其实是可以由编译器推导出来的,因为编译器在将函数模板实例化时就已经知道各个模板类型参数的实际类型:

template<typename X, typename Y>
void multiply(const X& x, const Y& y){
    auto result = x * y; // 如果没有auto,那么result的类型是没办法指定的
    ...
}

这个例子仅为了说明auto的用处,以后我说到其它C++11,14新语法时会再修改这个例子,让它看起来更合理。

到这里先简短总结下使用auto的理由:

  • 减少代码中的多余的类型名字
  • 保证类型安全,auto变量的类型与它的初始化表达式类型一致或相似(后面会解释)
  • 更好的抽象和面向接口变成,如果使用auto关键字,那么在写程序时会更关心变量的类型提供了什么行为,而不是它的名字是什么。
  • 简化代码重构工作
  • 简化模板代码,因为编译器可以帮我们推导出类型,而不需要我们显式指定
  • 可以将lambda表达式保存在一个变量中,因为lambda表达式的类型是由编译器决定的

深入auto

待续。。。

posted @ 2016-06-20 15:53  黄文俊  阅读(402)  评论(0)    收藏  举报