07 Aug 2017
C++11特性介绍——类型推导
类型推导
我们都知道, C++中, 类型有静态类型和动态类型的区别, C++在98中,提供了对了动态类型的支持, 那就是运行时的类型识别(RTTI); C++ 98中RTTI运算符只要是typeid
和 dynamic_cast
; 具体是用不再赘述;
RTTI的缺点
- RTTI不能否认的, 会在运行时带来一些开销; 所以一般编译器都会让用户选择关闭如: gcc中使用选项
-fno-rttion
控制; - 一般来说, 运行时候才确定类型对于我们来说有点为时过晚, 我们更过需要在编译时候确定类型, 这时候RTTI显然无法满足;
这时候类型推导很好地解决了上面问题! 类型推导在模板和泛型编程中广泛应用!
C++ 11中类型推导实现方式主要有两种:
- auto关键字;
- decltype;
auto
auto优势
使用auto做类型推导有几个优势(或者说几种最常见用法):
-
简化代码, 增加代码可读性;
void func(const std::vector<std::string>& names) { //相比较iter和iter1 std::vector<std::string>::iterator iter1 = names.begin(); for(auto iter = names.begin(); iter != names.end(); ++iter) { } }
-
auto的“自适应性”能够在一定程度上支持泛型编程;
//对于不同平台, strlen可能返回4字节整型, 也可能返回8字节整型 auto很好解决这个问题 //等同于size_t auto len = strlen("hello"); //在模板中, "自适应"体现的更加明显 template<typename T1, typename T2> double Sum(T1& t1, T2& t2) { auto s = t1 + t2; return s; } auto ret = Sum<int, float>(a, b); //s 推导为float auto ret1 = Sum<float, float>(c, b); //s 推导为float
auto使用细则
-
auto 和指针和引用结合使用;
int x = 1; int* y = &x; double foo(); int& bar(); auto * a = &x; //int* auto & b = x; //int& auto c = y; //int* auto * d = y; //int* //auto * e = &foo(); //失败 指针不能指向一个临时变量 //auto & f = foo(); //编译失败 nonconst的左值引用不能和一个临时变量绑定 auto & g = bar();
-
auto 和const, volatile结合使用; C++ 11规定auto可以和cv限制符一起使用, 但是声明为auto的变量不能从初始化表达式中”带走”cv限制符;
double foo(); float* bar(); const auto a = foo(); //const double const auto& b = foo(); //const double& volatile auto* c = bar(); //volatile float* auto d = a; //double auto& e = a; //const double& auto f = c; //float* volatile auto& g = c; //volatile float* &
-
auto 和初始化列表, new的配合使用;
auto y{1}; //y:int auto z = new auto(1); //z:int
-
auto使用于for循环中, auto所有用法中, 这个是我目前使用频率最高的;
int array[5] = { 1, 2, 3, 4, 5 }; for (const auto& e : array) { cout << e << "\t"; } cout << endl; vector<unsigned int> vec_for = { 11, 22, 33, 4 }; for (auto i = vec_for.begin(); i != vec_for.end(); ++i) { cout << dec << *i << "\t"; } cout << endl; for (const auto& e : vec_for) { cout << e << endl; //e是解引用后的对象 } cout << endl;
- auto 不能作为函数的形参; 编译不通过;
- 结构体中, 非静态成员变量不能是auto;
- 声明auto数组, 同样也不会编译通过;
-
实例化模板时候, 使用auto作为模板参数, 编译不通过;
std::vector<auto> v {1, 2}; //编译不通过
decltype
decltype的应用
-
与typedef, using的结合使用;
//我们在C++ 11头文件中经常见到这样的定义 using size_t = decltype(sizeof(0)); using ptrdiff_t = decltype((int*)0 - (int*)0); using nullptr_t = decltype(nullptr);
-
decltype用作变量的声明;
std::vector<std::string> names; typedef decltype(names.begin()) NamesIterator; for(NamesIterator iter = names.begin(); iter != names.end(); ++iter) { } //等价于上面 for(decltype(names)::iterator iter1 = names.begin(); iter != names.end(); ++iter) { }
-
C++中有时候我们也会经常遇到, 匿名类型; 拥有decltype后, 重用匿名类型也并非难事;
enum class { DEBUG, INFO, TRACE } LogLevel; decltype(LogLevel) level1;
-
decltype 与模板以及泛型编程的结合;
template<typename T1, typename T2> void Sum(const T1& t1, const T2& t2, decltype(t1 + t2)& s) { s = t1 + t2; }; int a = 3; long b = 5; long e = 0; Sum(a, b, e); //s 被推导为long
-
使用decltype追踪返回类型
template<typename T1, typename T2> auto Mul(const T1& t1, const T2& t2) -> decltype(t1 * t2) { return t1 * t2; }
decltype使用原则
这里使用实例decltype(e)
进行说明:
- 如果
e
是没有带括号的标记符表达式或者类成员访问表达式, 那么decltype(e)
就是e
所命名的实体的类型; - 如果
e
是一个被重载的函数, 则会导致编译错误; - 假设
e
的类型是T, 则decltype(e)
也是T; - 假设
e
的类型是T, 如果e
是左值, 则decltype(e)
是T&; - 假设
e
的类型是T, 如果e
是将亡值, 则decltype(e)
是T&&; - 与auto不同, auto类型推导时候不可以”带走”CV限制符, decltype推导则可以带走CV限制符;
//decltype使用 规则
int i = 4;
int arr[5] = { 0 };
int *ptr = arr;
struct S
{
double d;
} s;
//右值引用
int && RvalRef();
void Overloaded(int);
void Overloaded(char);
/*
规则1: 单个标记符表达式以及访问类成员 推导为本类型
*/
decltype(arr) var1; //int[5] 标记符表达式
decltype(ptr) var2; //int*
decltype(s.d) var3; //double
decltype(Overloaded) var4; //编译失败 因为重载
/*
规则2:将亡值 推导为类型的右值引用
*/
decltype(RvalRef()) var6 = 1; //int&&
/*
规则3:左值,推导为类型的引用
*/
decltype(true ? i : i) var7 = i; //int& 三元运算符返回一个i的左值
decltype((i)) var8 = i; //int& 带圆括号的左值
decltype(++i) var9 = i; //int& ++i 返回i的左值
decltype(arr[3]) var10 = i; //int& []返回左值
decltype(*ptr) var11 = i; //int& *返回左值
decltype("lval") var12 = "lval"; //const char(&)[9]
/*
规则4:以上都不是,推导为本类型
*/
decltype(1) var13; //int
decltype(i++) var14; //int i++返回右值
cuipf
