一、概要
在传统 C 和 C++中,参数的类型都必须明确定义,这其实对我们快速进行编码没有任何帮助,尤其是当我们面对一大堆复杂的模板类型时,必须明确的指出变量的类型才能进行后续的编码,这不仅拖慢我们的开发效率,也让代码变得又臭又长。
C++11 引入了 auto 和 decltype 这两个关键字实现了类型推导,让编译器来操心变量的类型。这使得 C++ 也具有了和其他现代编程语言一样,某种意义上提供了无需操心变量类型的使用习惯。
二、auto
auto 在很早以前就已经进入了 C++,但是它始终作为一个存储类型的指示符存在,与 register并存。在传统 C++ 中,如果一个变量没有声明为 register变量,将自动被视为一个auto变量。而随着register被弃用,对 auto 的语义变更也就非常自然了。
使用 auto进行类型推导的一个最为常见而且显著的例子就是迭代器。在以前我们需要这样来书写一个迭代器:
for(vector::const_iterator itr = vec.cbegin(); itr != vec.cend(); ++itr)
有了auto之后可以:
for(auto itr = vec.cbegin(); itr != vec.cend(); ++itr); // itr 被推导为 vector::const_iterator 类型
auto i = 5; // i 被推导为 int
auto arr = new auto(10) // arr 被推导为 int *
注意:auto不能用于函数传参,不能用于推导数组类型。
int main()
{
auto i = 5;
int arr[10] = {0};
auto auto_arr = arr;
auto auto_arr2[10] = arr; //编译报错
return 0;
}
三、decltype
decltype操作符的值是一个类型,可用于其它对象的声明。
decltype 关键字是为了解决 auto关键字只能对变量进行类型推导的缺陷而出现的。它的用法和sizeof 很相似:
decltype(表达式)
计算某个表达式的类型,如:
auto x = 1;
auto y = 2;
decltype(x+y) z;
注意:注意:decltype((variable))(注意是双层括号)的结果永远是引用,而decltype(variable)的结果只有当variable本身是一个引用时才是引用
decltype与auto都可以用来推断类型,但是二者有几处明显的差异
1)auto忽略顶层const,decltype保留顶层const。
2)对引用操作,auto推断出原有类型,decltype推断出引用。
3) 对解引用操作,auto推断出原有类型,decltype推断出引用。
4)auto推断时会实际执行,decltype不会执行,只做分析。总之在使用过程中和const、引用和指针结合时需要特别小心。
1. decltype用法
1)基本用法
int getSize() { return 0; };
int main(void)
{
int tempA = 2;
//1.dclTempA为int
decltype(tempA) dclTempA;
dclTempA = 10;
//2.dclTempB为int,对于getSize根本没有定义,但是程序依旧正常,因为decltype只做分析,并不调用getSize().
decltype(getSize()) dclTempB;
dclTempB = 11;
//3.dcltempC为string
string tempC = "abc";
decltype(tempC) dcltempC;
dcltempC = "efg";
return 0;
}
2)与const结合
int main(void)
{
double tempA = 3.0;
const double ctempA = 5.0;
const double ctempB = 6.0;
const double *const cptrTempA = &ctempA;
//1.dclTempA推断为const double(保留顶层const,此处与auto不同)
decltype(ctempA) dclTempA = 4.1;
//2.dclTempA为const double,不能对其赋值,编译不过
dclTempA = 5;
//3.dclTempB推断为const double * const
decltype(cptrTempA) dclTempB = &ctempA;
//4.输出为4(32位计算机)和5*/
cout << sizeof(dclTempB) << " " << *dclTempB << endl;
//5.保留顶层const,不能修改指针指向的对象,编译不过
dclTempB = &ctempB;
//6.保留底层const,不能修改指针指向的对象的值,编译不过
*dclTempB = 7.0;
return 0;
}
3)与引用结合
int main(void)
{
int tempA = 0, &refTempA = tempA;
//1.dclTempA为引用,绑定到tempA
decltype(refTempA) dclTempA = tempA;
//2.dclTempB为引用,必须绑定到变量,编译不过
decltype(refTempA) dclTempB = 0;
//3.dclTempC为引用,必须初始化,编译不过
decltype(refTempA) dclTempC;
//4.双层括号表示引用,dclTempD为引用,绑定到tempA
decltype((tempA)) dclTempD = tempA;
const int ctempA = 1, &crefTempA = ctempA;
//5.dclTempE为常量引用,可以绑定到普通变量tempA
decltype(crefTempA) dclTempE = tempA;
//6.dclTempF为常量引用,可以绑定到常量ctempA
decltype(crefTempA) dclTempF = ctempA;
//7.dclTempG为常量引用,绑定到一个临时变量
decltype(crefTempA) dclTempG = 0;
//8.dclTempH为常量引用,必须初始化,编译不过
decltype(crefTempA) dclTempH;
//9.双层括号表示引用,dclTempI为常量引用,可以绑定到普通变量tempA
decltype((ctempA)) dclTempI = ctempA;
return 0;
}
4)与指针结合
int main(void)
{
int tempA = 2;
int *ptrTempA = &tempA;
//1.常规使用dclTempA为一个int *的指针
decltype(ptrTempA) dclTempA;
//2.需要特别注意,表达式内容为解引用操作,dclTempB为一个引用,引用必须初始化,故编译不过
decltype(*ptrTempA) dclTempB;
return 0;
}
2. 什么时候使用decltype?
#include <iostream>
#include <string>
#include <stdio.h>
#include <vector>
#include <list>
#include <map>
using namespace std;
template<typename C>
void print(C& c)
{
for (decltype (c.begin()) it = c.begin(); it != c.end(); ++it)
cout << *it << ' ';
cout << endl;
}
int main(void)
{
int ai[] = { 10, 20, 30,40, 50 };
vector<int> vi(ai, ai + 5);
print(vi);
list<int> const li(vi.begin(), vi.end());
print(li);
map<string, vector<int>> msv;
msv["张飞"].push_back(70);
msv["张飞"].push_back(85);
msv["赵云"].push_back(75);
msv["赵云"].push_back(90);
msv["关羽"].push_back(80);
msv["关羽"].push_back(95);
// 此处省略15000行代码
// int sum = 0;
//key_type就表示map中key的类型,value_type表示具体值的类型,mapped_type表示map中value(pair)的类型
decltype (msv)::mapped_type::value_type sum = 0;
for (size_t i = 0; i < msv["张飞"].size();++i)
sum += msv["张飞"][i];
cout << sum << endl;
return 0;
}
3. auto和decltype结合使用,返回类型后置
#include <iostream>
#include <typeinfo>
using namespace std;
double foo(int arg)
{
return arg / 2.0;
}
int foo(double arg)
{
return int(arg * 2);
}
// 返回类型后置
template<typename T>
auto bar(T const&arg) -> decltype (foo(arg))
{
return foo(arg);
}
// 返回类型后置
template<typename T>
auto add(T const&x, T const& y) -> decltype (x + y)
{
return x + y;
}
class Point
{
public:
Point(int x, int y) : m_x(x), m_y(y) {}
void draw(void) const
{
cout << "点(" << m_x << ',' << m_y << ')' << endl;
}
private:
int m_x, m_y;
};
class Line
{
public:
Line(Point const& p1, Point const&p2) :m_p1(p1), m_p2(p2) {}
void draw(void) const
{
cout << "线(" << '\t';m_p1.draw();
cout << '\t';
m_p2.draw();
cout << ')' << endl;
}
private:
Point m_p1, m_p2;
};
Line const operator+ (Point const& p1, Point const&p2)
{
return Line(p1, p2);
}
int main(void)
{
cout << bar(1) << endl; // 0.5
cout << bar(0.5) << endl; // 1
Point p1(100, 200), p2(300, 400);
Line line = add(p1, p2);
line.draw();
return 0;
}
结果输出: