C++11初探:类型推导,auto和decltype
2017-09-27 22:03
609 查看
这篇文章转载自http://www.cnblogs.com/npbool/p/3433360.html。
类型推导可以说是C++模拟动态语言特性的起点,就从这里开始这个系列吧。
使用迭代器的时候,类型总是一件烦心的事。
函数指针也同样, 类型声明很蛋疼:
我既然把v.begin()赋给it, 类型已经在编译期确定了,编译器知道正确的类型是什么,再加一个类型声明实在很繁琐。C++11 有了auto。我们可以这样写:
编译器会根据值的类型,推导出autob变量。类型的推导是在编译期就完成的,仍是静态类型,和脚本语言不同。实际上是一个语法糖。但由于C++对模板的大量使用,一个变量的类型有时过于复杂难以写出,这样的语法糖是必要的。
很简单,不是么?(实际会有一些细节问题,文末再说)
如果我们只是想用一个编译器推导的类型声明一个变量,但不初始化,auto无能为力。此时可以用decltype。
需要说明,decltype仍然是编译期行为,add()函数不会真正执行,只是利用它推导了类型。
考虑一个模板函数
总之我要在这个函数做点事情,最后返回一个u*v类型的东西。返回类型该怎么写?
有了decltype,似乎可以这么:
但是不行!u,v不在作用域。
u,v不能用,自己搞出一个可以用的U,V类型的变量总行了吧XD
试一下,foo(2,3.3)返回6.6,貌似正常。但是!你怎么知道U,V可以接受一个0作为构造参数的?U,V不一定是数值,有可能是重载了'*'操作符的奇怪东西。没关系,指针总是可以这样初始化的:
但是你不觉得太丑了么。。。
C++11引入了返回类型后置解决这个问题,
语法很清晰。总之就是,返回类型后置以后,就进入函数的作用域了,参数都能用了!
另外还有一个用处,假设你要写个类
那个A::B怎么看都别扭,用返回类型后置,可以这样:
再来一次,后置的返回类型,处在函数作用域里,B已经可见,不需要A::修饰了。
但你肯定有一个问题,既然我函数代码已经给出了,编译器完全可以推断出返回类型是什么,为什么还要自己显式给出返回类型?如果能这样就好了:
但是很遗憾,不行。由于“复杂性”,标准没有这样做。这也容易理解,假如你的程序编译成了库,只给了别人头文件里的函数签名,函数体对编译器是不可见的,那你这个auto算什么意思?
但事情也不那么绝对,通过匿名函数返回值类型可省略这个特性加上auto,有些情况下我们的确可以不用写返回类型。以后再谈。
虽然我很不想写细节,但C++的东西总不是那么简单,为了完整性,把这些黑暗角落放到最后,希望不会影响对以上特性的好感...
auto+引用
直观上感觉,r是i的引用,a自然也会推导出int&这个类型。但实际上,r只是i的别名,a类型的推导结果还是int。于是,对a的修改和i无关,结果输出0。如果想推导出引用,需要这样
auto+const
还有一点,auto会忽略顶层的const。
其中,a是普通的int, 因为i是const int,auto把顶层的const忽略了。b是const int*, 这个可以这样看,const int*实际上是“(const int)*”(当然代码不能这样写),const不是顶层修饰符了,就没有忽略。
如果想得到推导出一个const类型,需要
另外,decltype和auto不同,它不会忽略引用和顶层const修饰。
最后,比较特别的是,如果decltype里面的表达式被包含在括号中,视为对表达式求值的类型。
对i求值返回的是int&。
类型推导可以说是C++模拟动态语言特性的起点,就从这里开始这个系列吧。
auto
使用迭代器的时候,类型总是一件烦心的事。vector<vector<int> > v; vector<vector<int> >::iterator it = v.begin();
函数指针也同样, 类型声明很蛋疼:
int add(int x,int y){ return x+y; } int main(){ int (*func)(int,int) = add; cout<<func(1,2)<<endl; }
我既然把v.begin()赋给it, 类型已经在编译期确定了,编译器知道正确的类型是什么,再加一个类型声明实在很繁琐。C++11 有了auto。我们可以这样写:
vector<vector<int>> v; // C++11 可以不用在'>>'之间加空格了! auto it = v.begin(); auto func = add;
编译器会根据值的类型,推导出autob变量。类型的推导是在编译期就完成的,仍是静态类型,和脚本语言不同。实际上是一个语法糖。但由于C++对模板的大量使用,一个变量的类型有时过于复杂难以写出,这样的语法糖是必要的。
很简单,不是么?(实际会有一些细节问题,文末再说)
decltype
如果我们只是想用一个编译器推导的类型声明一个变量,但不初始化,auto无能为力。此时可以用decltype。int add(int x,int y){ return x+y; } int main(){ double i=0; decltype(i) a; // double decltype(add()) b; //int 注意括号。不带括号就是函数指针了。 }
需要说明,decltype仍然是编译期行为,add()函数不会真正执行,只是利用它推导了类型。
返回类型后置
考虑一个模板函数template<typename U, typename V> ??? foo(U u, V v){ ... return u*v; }
总之我要在这个函数做点事情,最后返回一个u*v类型的东西。返回类型该怎么写?
有了decltype,似乎可以这么:
template<typename U, typename V> decltype(u*v) foo(U u, V v){ //WRONG! return u*v; }
但是不行!u,v不在作用域。
u,v不能用,自己搞出一个可以用的U,V类型的变量总行了吧XD
template<typename U, typename V> decltype(U(0)*V(0)) foo(U u, V v){ return u*v; }
试一下,foo(2,3.3)返回6.6,貌似正常。但是!你怎么知道U,V可以接受一个0作为构造参数的?U,V不一定是数值,有可能是重载了'*'操作符的奇怪东西。没关系,指针总是可以这样初始化的:
template<typename U, typename V> decltype(*(U*)(0)**(V*)(0)) foo(U u, V v){ return u*v; }
但是你不觉得太丑了么。。。
C++11引入了返回类型后置解决这个问题,
template<typename U, typename V> auto foo(U u, V v) -> decltype(u*v){ return u*v; }
语法很清晰。总之就是,返回类型后置以后,就进入函数的作用域了,参数都能用了!
另外还有一个用处,假设你要写个类
//A.h struct A { struct B {}; B func(); }; //A.cpp A::B A::func(){ return B(); }
那个A::B怎么看都别扭,用返回类型后置,可以这样:
auto A::func() -> B { return B(); }
再来一次,后置的返回类型,处在函数作用域里,B已经可见,不需要A::修饰了。
但你肯定有一个问题,既然我函数代码已经给出了,编译器完全可以推断出返回类型是什么,为什么还要自己显式给出返回类型?如果能这样就好了:
template<typename U, typename V> auto foo(U u, V v){ return u*v; }
但是很遗憾,不行。由于“复杂性”,标准没有这样做。这也容易理解,假如你的程序编译成了库,只给了别人头文件里的函数签名,函数体对编译器是不可见的,那你这个auto算什么意思?
但事情也不那么绝对,通过匿名函数返回值类型可省略这个特性加上auto,有些情况下我们的确可以不用写返回类型。以后再谈。
一些细节
虽然我很不想写细节,但C++的东西总不是那么简单,为了完整性,把这些黑暗角落放到最后,希望不会影响对以上特性的好感...auto+引用
int i = 0; int& r = i; auto a = r; // type of a? a = 10; cout<<i<<endl; // value of i?
直观上感觉,r是i的引用,a自然也会推导出int&这个类型。但实际上,r只是i的别名,a类型的推导结果还是int。于是,对a的修改和i无关,结果输出0。如果想推导出引用,需要这样
auto& a = r; // auto& a = i也可以
auto+const
还有一点,auto会忽略顶层的const。
const int i = 0; auto a = i; // a: int audo b = &i // b: const int*
其中,a是普通的int, 因为i是const int,auto把顶层的const忽略了。b是const int*, 这个可以这样看,const int*实际上是“(const int)*”(当然代码不能这样写),const不是顶层修饰符了,就没有忽略。
如果想得到推导出一个const类型,需要
const auto a = i; // a: const int
另外,decltype和auto不同,它不会忽略引用和顶层const修饰。
const int i=0; int j=0, &r = j; decltype(i) a; //const int decltype(r) b; //int&
最后,比较特别的是,如果decltype里面的表达式被包含在括号中,视为对表达式求值的类型。
decltype((i)) a;//error: a is int& and must be initialized
对i求值返回的是int&。
相关文章推荐
- C++11初探:类型推导,auto和decltype
- C++11系列-改进的类型推导:auto、decltype和新的函数语法
- C++11系列-改进的类型推导:auto、decltype和新的函数语法
- c++11——auto,decltype类型推导
- C++11系列-改进的类型推导:auto、decltype和新的函数语法
- C++11系列-改进的类型推导:auto、decltype和新的函数语法
- C++11新特性——auto和decltype类型推导
- C++11系列-改进的类型推导:auto、decltype和新的函数语法
- C++11系列-改进的类型推导:auto、decltype和新的函数语法
- C++11系列-改进的类型推导:auto、decltype和新的函数语法
- C++11读书笔记—3(auto与decltype类型推导)
- C++ 11 学习1:类型自动推导 auto和decltype
- 杂货边角(12):C++11动态性来源之类型推断 && auto\decltype\追踪返回类型
- C++11 类型推导decltype
- C++11:类型推导和追踪函数返回类型decltype
- C++11:类型推导auto
- 类型推导(auto, decltype)
- c++11之auto自动类型推断和decltype类型获取
- C++11 类型推导auto
- [C++]高效使用c++11--理解auto类型推导