【C++11】auto与decltype关键字

       在我们编程时候常常需要把表达式的值赋给变量,需要在声明变量的时候清楚的知道变量是什么类型。然而做到这一点并非那么容易(特别是模板中),有时候根本做不到。

       为了解决这个问题,C++11新标准就引入了auto类型说明符,用它就能让编译器替我们去分析表达式所属的类型。和原来那些只对应某种特定的类型说明符(例如 int)不同。auto 让编译器通过初始值来进行类型推演。从而获得定义变量的类型,所以说 auto 定义的变量必须有初始值。因此auto并非是一种“类型”的声明,而是一个类型声明时的“占位符”,编译器在编译期会将auto替换为变量实际的类型。

auto关键字:

1、在拥有初始化表达式的复杂类型变量声明时的简化:

//在拥有初始化表达式的复杂类型变量声明时的简化
void TestAuto()
{
	std::vector<int> v{ 1,2,3,4,5,6,7,8,9 };
	std::vector<int>::iterator it = v.begin();
	while (it != v.end())
		cout << *it++ << " ";
	cout << endl;
	for (auto it = v.begin(); it != v.end(); ++it)
		cout << *it << " ";
	cout << endl;
}
int main()
{
	TestAuto();
	system("pause");
	return 0;
}

2、免除程序员在一些类型声明时的麻烦,或者避免一些在类型声明时的错误:

int main()
{
	const float p = 3.14f;
	double r = 2.0;
	auto c = 2 * p * r;
	cout << c << endl;
	cout << typeid(c).name() << endl;
	system("pause");
	return 0;
}

3、 auto的自适应性能够在一定程度上支持泛型编程:

//auto的自适应性能够在一定程度上支持泛型编程
template<class T1, class T2>
double Add(const T1& left, const T2& right)
{
	auto ret = left + right;
	cout << typeid(ret).name() << endl;
	return ret;
}
int main()
{
	auto a = Add(1, 2);
	auto b = Add(1.0f, 2.0f);
	system("pause");
	return 0;
}

结果如图:

4、auto和指针一起使用的时候:

//auto和指针一起使用的时候
int main()
{
	int x = 10;
	auto a = &x;
	auto* b = &x;
	auto& c = x;
	//auto声明为某一个变量的引用,必须加&
	cout << typeid(a).name() << endl;
	cout << typeid(b).name() << endl;
	cout << typeid(c).name() << endl;
	*a = 20;
	*b = 30;
	c = 40;
	system("pause");
	return 0;
}

C++11中vector的简单应用:


//vector的初始化
int main()
{
	vector<int> va;
	for (int i = 0; i < 5; i++)
		va.push_back(i);//把五个元素放进去

	vector<int> vb(va);//用va初始化vb,要求同一容器,类型相同
	vector<int> vc{ 1,2,3,4 };//初始化列表(c++11)
	vector<int> vd = { 1,2,3,4 };
	vector<int> ve(va.begin(), va.end());//迭代器指定范围初始化
	return 0;
}

//利用拷贝构造函数和swap函数可以实现赋值操作
int main()
{
	vector<int> va, vb, vc;
	va = { 1,2,3,4 };
	for (int i = 0; i < 10; i++)
	{
		vb.push_back(i);
	}
	va = vb;//vb拷贝给va
	swap(va, vc);
	for (auto x : vc)//输出应该是va里面的值
		cout <<x << "";
	for (auto x : va)//va为空
		cout <<x << "";
	cout << endl;
	va.swap(vc);//再把vc和va的值换回来
	for (auto x : vc)
		cout <<  x << "";//里面为空
	for (auto x : va)
		cout <<  x << "";
	system("pause");
	return 0;
}

//vector传统遍历(下标,迭代器)
int main()
{
	ios::sync_with_stdio(false);
	vector<vector<int>> vvb(4, { 1,2,3,4 });
	for (int i = 0; i != vvb.size(); i++)
	{
		for (int j = 0; j != vvb[i].size(); j++)
			cout << vvb[i][j] << " ";
		cout << endl;
	}
	for (auto bit = vvb.begin(); bit != vvb.end(); bit++)
	{
		for (auto bbit = bit->begin(); bbit != bit->end(); bbit++)//bit类似指向一个vvb[i],里面是向量中的元素
			cout << *bbit << " ";
		cout << endl;
	}
	system("pause");
	return 0;
}

//C++11中vector遍历:
int main()
{
	vector<vector<int>> vvb(4, { 1,2,3,4 });
	for (auto x : vvb)
	{
		for (auto xx : x)
			cout << xx << ""<<endl;
	}
	cout << endl;
	system("pause");
	return 0;
}

auto总结:

优势:

1、在拥有初始化表达式的复杂类型变量声明时的简化 

2、可以免除程序员在一些类型声明时的麻烦,或者避免一些在类型声明时的错误

3、auto的自适应性能够在一定程度上支持泛型编程

auto不能推导出来的场景

auto不能作为函数的形参类型

auto不能定义类的非静态成员变量

auto不能直接用来声明数组

实例化模板时不能使用auto作为模板参数 

缺陷:(可读性变差),必须要对auto声明的类型进行初始化。当同一行声明多个变量时,这些变量必须是相同的类型。

 

decltype关键字:

      decltype关键字和auto相互对应的,它们经常在一些场所配合使用。decltype可以在编译的时候判断出一个变量或者表达式的类型。

      C++98中确实已经支持RTTI,比如:typeid和dynamic_cast,但typeid只能查看类型不能用其结果类定义类型,而dynamic_cast只能应用于含有虚函数的继承体系中,而且运行时类型识别的缺陷是降低程序运行的效率,因此C++11又推出了decltype关键字:

struct
{
	int _x;
	int _y;
}pt;
int main()
{
	decltype(pt) p;
	cout << typeid(p).name() << endl;
	cout << offsetof(decltype(pt), _y) << endl;
	system("pause");
	return 0;
}

void* GetMemory(size_t size)
{
	return malloc(size);
}
int main()
{
	//  如果没有带参数,推导函数的类型
	cout << typeid(decltype(GetMemory)).name() << endl;
	//  如果带参数列表,推导的是函数返回值的类型
	cout << typeid(decltype(GetMemory(0))).name() <<endl;
	system("pause");
		return 0;
}

auto和decltype一起使用的场景:

template <class T, class U>
auto add(T t, U u) ->decltype(t + u)
{
	return t + u;
}
int main()
{
	auto r = add(1, 1.0);
	cout << typeid(r).name() << endl;
	system("pause");
	return 0;
}

decltype推导的四个规则:

    假设decltype需要推导表达式e的类型,如果最后推导出来的类型和用户原期望的有差别,可以从参考以下原则:

     1、如果e是一个没有带括号的标记符表达式或者类成员访问表达式,那么decltype(e)就是e所命名的实体的类型。此外如果e是一个被重载的函数,则会编译失败。

    2、标记符表达式:单个标记符对应的表达式即为标记符表达式。(除去关键字、字面常量等编译器需要用到的标记)比如:int arr[3];arr为标记符表达式,但是arr[0] + 3和arr[3]就不是。

    3、否则,假设e的类型是T,如果e是将亡值(比如临时变量或者函数局部变量)decltype(e)被推导为T&&,否则,假设e的类型是T,如果e是一个左值,则decltype(e)为T的引用。否则,假设e的类型是T,则decltype(e)为T。

猜你喜欢

转载自blog.csdn.net/yaotengjian/article/details/81539063