C++11——类型推导(auto和decltype的使用)

1.1 类型推导

1.1.1 auto类型推导

1.auto关键字的新意义
auto关键字是一个类型指示符,用来提示编译器对此类型的变量做类型的自动推导。
auto x = 5; //OK:x是int 类型
auto pi = new auto(1); //OK:pi被推导为int*
const auto v = &x,u = 6; //OK:v是const int 类,iu是const int类型
static auto y = 0.0; //OK:y是double类型
auto int r; //error:auto不再表示存储类型提示符
auto s; //error:auto 无法推导出s的类型
2.auto的推导规则
它可以同指针,引用结合起来使用,还可以带上cv限定符(cv-qualifier,const和volatile限定符的统称)
int x = 0;
auto * a = &x; //a->int*,auto被推导为int
auto b = &x; //b->int*,auto被推导为int*
auto &c = x; //c->int&被推导为int
auto d = c; //d->int,auto被推导为int

const auto e = x; //e->const int
auto f = e; //f->int

const auto g = x; //g->const int &
auto& h = g; //h->const int&
有以下规则:
(1)当不声明为指针或引用时,auto的推导结果和初始化表达式抛弃引用和cv限定符后类型一致。
(2)当声明为指针或引用时,auto的推导结果将保持初始表达式的cv属性。
3,auto的限制

void func(auto a = 1){}		//error:auto不能用于函数参数
struct Foo
{
auto var1_ = 0;			//error:auto不能用于非静态成员变量
static const auto var2_ = 0;	//OK:var2_->static const int
};
template <typename T> 
struct Bar{};
int main()
{
int arr[10] = {0}
auto aa = arr;		//OK:aa->int *
auto rr[10] = arr;	//error:auto无法定义数组
Bar<int> bar;		
Bar<auto> bb = bar;	//error:auto 无法推出模板参数
return 0}

4,什么时候用auto
在C++11之前,定义了一个stl容器后,在遍历的时候常常会写这样的代码:

#include<map>
int main(void)
{
std::map<double,double> resultMap;
//...
std::map<double,double>::iterator it = resultMap.begin();
for(;it != resultMap.end();++it)
{
//do something
}
return 0;
}

使用了auto之后的写法:

#include<map>
int main(void)
{
std::map<double,double> resultMap;
//...
for(auto it = resultMap.begin();it != resultMap.end();++it)
{
//do something
}
return 0;
}

1.1.2 decltype关键字

1.获知表达式的类型
C++新增了decltype关键字,用来编译时推导出一个表达式的类型。它的语法格式如下:
decltype(exp)
其中,exp表示一个表达式(expression)。

int x = 0;
decltype(x) y = 1;	//y->int
decltype(x+y) z = 0;	//z->int

const int& i = x;
decltype(i) j = y;	//j->const int &

const decltype(z) * p= &z;		//*p->const int, p->const int *
decltype(z)* pi = &z;			//*pi->int,pi->int *
decltype(pi) *pp = &pi;			//*pp->int*,pp->int**

2.decltype的推导规则
推导规则1:exp是标识符,类访问表达式,decltype(exp)和exp的类型一致
推导规则2:exp是函数调用,decltype(exp)和返回值类型一致
推导规则3:其他情况,若exp是一个左值,则decltype(exp)是exp类型的左值引用,否则和exp类型一致。
(1)标识符表达式和类访问表达式

class Foo
{
public:
	static const int Number = 0;
	int x;
 } ;
 int n = 0;
 volatile const int & x = n;
 
 decltype(n) a = n;		//a->int 
 decltype(x) b = n;		//a->const volatile int& 
 
 decltype(Foo::number) c = 0;	//c->const int
 
 Foo foo;
 decltype(foo.x) d = 0;		//d->int ,类访问表达式 

a,b,c,d均符合推导规则1
(2)函数调用

int& func_int_r(void);		//左值(lvalue,可简单理解为可寻址值)
int&& func_int_rr(void);	//x值(xvalue,右值引用本身是一个xvalue)
int func_int(void);			//纯右值(prvalue,后续内容)

const int& func_cint_r(void);	//左值
const int&& func_cint_rr(void);	//右值
const int func_cint(void);		//纯右值

const Foo func_cfoo(void);		//纯右值

//下面是测试语句
int x = 0;

decltype(func_int_r())	a1 = x;		//a1->int & 
decltype(func_int_rr())	b1 = 0;		//b1->int && 
decltype(func_int())	c1 = 0;		//c1->int  

decltype(func_cint_r())	a2 = x;		//a2->const int & 
decltype(func_cint_rr())b2 = 0;		//b2->const int && 
decltype(func_cint())	c2 = 0;		//c2->int  

decltype(func_cfoo())	ff = Foo();		//ff->const Foo

按照推导规则2,decltype的结果和函数的返回值类型保持一致。这里需要注意的是,c2是int 而不是 const int。这是因为函数返回的int是一个纯右值。对于纯右值,只有类类型可以携带cv限定符,此外则一般忽略掉cv限定符。
(3)带括号的表达式和加法运算表达式

struct Foo{
	int x;
};
const Foo foo = Foo();

decltype(foo.x) a = 0;		//a->int
decltype((foo.x)) b = a;	//b->const int &

int n = 0, m= 0;
decltype(n+m) c = 0;	//c->int
decltype(n+=m)d = c;	//d->int&

a的结果根据推导规则1,a的类型就是foo.x的定义类型。
b的结果不使用推导规则1和推导规则2.根据foo.x是一个左值,可知括号表达式也是一个左值。因此可以按照推导规则3知道decltype的结果将是一个左值引用。
foo的定义是const Foo,所以foo.x是一个const int 类型左值,因此decltype的推导结果为const int &.
同样n+m返回一个右值,按照推导规则3,decltype的结果为int
n+=m返回一个左值,按照推导规则3,decltype的结果为int&。
3,decltype的实际应用
范式编程定义可能存在问题的实例

#include <vector>
template <class ContainerT>
class Foo{
	typename ContainerT::iterator it_;	//类型定义可能有问题
public:
	void func(ContainerT& container)
	{
		it_ = container.begin();
	 } 
	 //...
};
int main(void)
{
	typedef const std::vector<int> container_t;
	container_t arr;
	
	Foo<container_t> foo;
	foo.func(arr);
	
	return 0;
}

ContainerT::iterator并不能包括所有的迭代器类型,当ContainerT是一个const类型时,应当使用const_iterator,可以增加一个模板特化:

template <class ContainerT>
class Foo<const ContainerT>{
	typename ContainerT::const_iterator it_;
public:
	void func(Const ContainerT& container)
	{
		it_ = container.begin();
	 } 
	 //...
};

若const类型的特化只是为了配合迭代器的类型限制,Foo的其他代码也不得不重新写一次。有了decltype,就可以这样写:

template <class ContainerT>
class Foo{
	decltype(ContainerT().begin()) it_;
public:
	void func(ContainerT& container)
	{
		it_ = container.begin();
	 } 
	 //...
};

1.1.3返回类型后置语法——auto和decltype的结合使用

·考虑一下情景:

template <typename R,typename T,typename U>
R add(T t, U u)
{
	return t+u;
}
int a = 1; float b = 2.0;
auto c = add<decltype(a+b)>(a,b);

我们并不关心a+b的类型是什么因此,只需要通过decltype(a+b)直接返回值类型即可。但是上述方法太过不方便。可以尝试:

template <typename T,typename U>
decltype(t+u) add(T t, U u)	//error:t,u尚未定义 
{
	return t+u;
}

显然编译不通过,因为在t,u在参数列表中,C++的返回值是前置语法,在返回值定义的时候参数变量还不存在。
可行的写法如下:

template <typename T,typename U>
decltype(T()+U()) add(T t, U u)	
{
	return t+u;
}

考虑到T,U可能是无参构造函数的类,正确的写法应该是:

template <typename T,typename U>
decltype((*(T*)0)+(*(U*)0)) add(T t, U u)	
{
	return t+u;
}

在C++11中增加了返回类型后置的语法,将delctype和auto结合起来完成返回值的推导。
因此,上述add函数可以写成:

template <typename T,typename U>
auto add(T t, U u)	->decltype(t+u)
{
	return t+u;
}
  • 0
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值