突破编程_C++_C++11新特性(auto与decltype)

本文介绍了C++11中auto关键字的新用途——自动类型推导,以及decltype的关键字如何在编译期进行类型推导。文章详细解释了auto和decltype的使用规则,包括它们在变量声明、函数返回值、引用处理和模板中的应用。
摘要由CSDN通过智能技术生成

1 auto

1.1 引入

在C++11之前,auto关键字主要用于自动存储类说明符,但在C++11中,其含义发生了改变。现在,auto关键字用于自动类型推导,由编译器根据初始化表达式的类型自动推断变量的类型。特别是在声明的变量的类型未知的情况下(比如在模板编程),auto关键字可以让编译器替我们去分析表达式所属的类型。

1.2 auto关键字基本使用方式

C++引入auto关键字主要有两种用途:
(1)在变量声明时根据初始化表达式自动推断该变量的类型。

int i = 0, &ri = i;

auto a = i; //a为int型变量
auto a1 = ri; //a1为int型变量

auto p = &i;// &i 是一个普通int指针,p是一个整型指针int *
auto p1 = &ri; //同上

(2)在声明函数时作为函数返回值的占位符。

template <typename _Tx, typename _Ty>
auto multiply(_Tx x, _Ty y)->decltype(x*y)
{
    return x*y;
}

1.3 auto关键字的推导规则

1.3.1 常规推导规则

(1)基本数据类型
编译器会根据初始化表达式的类型来推导变量的类型。

auto i = 1;  			// int
auto d = 2.66;  		// double
auto s = "test";  		// const char*

(2)引用类型
auto关键字也可以用于引用类型的推导。但是,需要注意的是,当使用auto&时,编译器会保留引用的类型。

int x = 2;

auto z = x;  			// int
auto& y = x;  			// int&
auto h = y;  			// int

(3)常量类型
当使用auto关键字推导常量类型时,需要特别注意:如果不使用const关键字,那么auto关键字不会保留常量性。

int x = 2;

const auto m = x;  		// const int
auto n = m;  			// int
auto& w = x;  			// const int&

如上,变量 m 的推导规则说明当表达式是一个引用类型时, auto 会把引用类型抛弃,直接推导成原始类型。
变量 n 的推导规则说明当表达式带有 const 时, auto 会把 const 属性抛弃,直接推导成 non-const 类型。
变量 w 的推导规则说明当 auto 与引用结合时, auto 的推导将保留const属性。
从而可以总结出,针对引用类型,当 auto 后不带引用,则推导结果将抛弃引用以及 const 属性( volatile 也一样);当 auto 与引用结合使用时,推导结果将保留原表达式的引用以及 const 属性( volatile 也一样)。

1.3.2 变量中的auto推导

auto 关键字提供了一种简洁的方式来声明变量,而不必明确指定其类型。这种类型推导的能力在某些情况下非常有用,但也可能带来一些意想不到的结果。
(1)初始化列表
如下,使用初始化列表来声明一个变量:

auto vals = {1, 2, 3, 4, 5, 6}; 

vals 的类型被推导为 std::initializer_list。
如果为初始化列表提供一个明确的类型时,情况会有所不同:

auto vals = std::vector<int>{1, 2, 3, 4, 5};  

在这种情况下,vals 的类型被正确地推导为std::vector。
(2)带有表达式的推导
如下,使用初始化列表来声明一个变量:

auto vals = {1, 2, 3, 4, 5, 6}; 

vals 的类型被推导为std::initializer_list。
如果为初始化列表提供一个明确的类型时,情况会有所不同:

auto vals = std::vector<int>{1, 2, 3, 4, 5};  

在这种情况下,vals 的类型被正确地推导为std::vector。

1.4 auto关键字的限制

(1)auto 关键字不能用于函数参数

void func(auto para){}			//error

(2)auto 关键字不能作用于类的非静态成员变量

class A
{
public:
	A(){}
	~A(){}
	
private:
	auto var1=0;				//error
	static auto var2=0;			//OK,var2的类型为int
}

(3)auto 关键字不能定义数组

 int a[10];    
 auto b = a;    //OK, b -> int*
 auto c = a;    //error

1.5 auto关键字的常见使用场景

(1)带有模板类型的变量
以如下容器类的代码为例:

#include <map>

int main()
{
	std::map<int,std::string> testMap;
	
	//使用详细类型
	std::map<int,std::string> it1 = testMap.begin();
	
	//使用 auto
	auto it2 = testMap.begin();
	
	......
}

一般而言,模板类型会比较长,如果使用 auto 关键字,则会极大的简化代码,不只方便书写,而且阅读代码也会变得清爽。
更为重要的是,一旦类型变更,则只需修改定义处,其他使用auto都不用调整。
(2)无法确定变量类型的情况
如下代码所示:

class A
{
public:
	A() {}
	~A() {}

public:
	std::string getVal()
	{
		return m_val;
	}

private:
	std::string m_val;
};

class B
{
public:
	B() {}
	~B() {}

public:
	int getVal()
	{
		return m_val;
	}

private:
	int m_val;
};

template<class T>
class C
{
public:
	C() {}
	~C() {}

public:
	//此处如果不使用 auto 关键字,则需要给该函数再增加一个模板参数,并且在调用时还需要手动指定返回类型
	auto getVal()
	{
		return m_obj.getVal();
	}

private:
	T m_obj;
};

在上面代码中,getVal() 函数的返回值使用 auto 关键字,从而避免了大量复杂代码的书写。

2 decltype

2.1 引入

decltype 是"declare type"的缩写,译为"声明类型"。和 auto 的功能一样,该关键字用来在编译时期进行自动类型推导。引入 decltype 是因为 auto 并不适用于所有的自动类型推导场景,在某些特殊情况下 auto 用起来很不方便,甚至压根无法使用。也可以将 decltype 看作是 sizeof 运算符的另一种形式,因为两者都不会真正计算其参数,只充当一种编译期工具的角色。

2.2 decltype 关键字基本使用方式

decltype 的语法格式如下:

decltype(exp)

其中,exp表示一个表达式(expression)。如下为 decltype 的使用样例:

int x = 1;
decltype(1)  			a = 2; 		//a -> int
decltype(x)  			b = 2; 		//b -> int
decltype(a+b)  			c;     		//c -> int

const int& y = 1;
decltype(y)  			d = 2; 		//d -> const int&
const decltype(x)*  	e = &x; 	//e -> const int*

int func(int, char);
decltype (func(1, 'C')) f; 			//表达式为函数的时候,推导结果为返回值类型。注意函数需要带参,但不会真的运行函数

对于变量 c ,该变量可以不用初始化,这是和auto的不同之处。对于变量 d ,该变量会保留 y 的 const & 属性,这也是和auto的不同之处。变量 e 的推导表明 decltype 可以像 auto 一样,加上引用和指针,以及 cv 限定符。

2.3 decltype 关键字的推导规则

针对decltype 的语法格式 decltype(exp) ,其语法规则有如下三点:
(1)当 exp 是标识符、类访问表达式时, decltype(exp) 和 exp 的类型一致。

int x = 1;
decltype(x)  			a = 2; 		//a -> int (标识符)
const int& y = 1;
decltype(y)  			b = 2; 		//b -> const int& (标识符)

class A
{
public:
	static int val1;
	int val2;
}
decltype(A::val1)  		c = 2; 		//c -> int (类访问表达式)
A z;
decltype(z.val2)  		d = 2; 		//d -> int (类访问表达式)

(2)当 exp 是函数调用时, decltype(exp) 和 exp 的返回值类型一致。

int func1(void);
decltype (func()) a; 			//a -> int

int& func1(void);
decltype (func()) b; 			//b -> int&

(3)当 exp 是一个运算表达式,如果 exp 是一个左值,则 decltype(exp) 是 exp 类型的左值引用,否则其类型和 exp 的类型一致。

class A
{
public:
	int val;
}
const A x = A();
decltype(x.val)  		a = 2; 		//a -> int
decltype((x.val))  		b = 2; 		//b -> const int& (x.val是左值,括号表达式也是一个左值,因此推导后即为一个左值引用)

int y=0;
int z=1;
decltype(y+z)  			c = 2; 		//c -> int (y+z返回一个右值,因此推导后即为一个左值)
decltype(y+=z)  		d = 2; 		//d -> int & (y+=z返回一个左值,因此推导后即为一个左值引用)

2.4 decltype 与 auto 结合使用

有如下场景:

template <class A, class B>
decltype(a + b) add(A a, B b) //错误,a、b 在推导的时候还没有被定义
{
    return a + b;
}

这段代码编译不过,因为a、b在参数列表中,C++的返回值是前置语法,这里先推导返回值类型是找不到a、b的定义。
将其修改为如下代码:

template <class A, class B>
auto add(A a, B b) -> decltype(a + b)
{
    return a + b;
}

通过将返回类型修改为后置语法,即可成功编译通过。

  • 24
    点赞
  • 20
    收藏
    觉得还不错? 一键收藏
  • 1
    评论
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值