auto和decltype的规则

auto

C++是一种静态强类型语言,任何一个变量都要有确定的类型,否则不能用。所以编译器必须知道类型,使用auto就是让编译器告诉类型

auto必须要有表达式才能推导,并且推导也不是完全正确。比如auto s = "hello"; 推导出来的是const char *,而不是string

所以auto用于初始化的场合,就是赋值初始化或者列表初始化,变量右边必须要有一个表达式。

特殊情况,在类成员变量初始化的时候,目前的 C++ 标准不允许使用 auto 推导类型

auto基本的规则:

  • auto 总是推导出“值类型”,绝不会是“引用”;
  • auto 可以附加上 const、volatile、*、& 这样的类型修饰符,得到新的类型
  • auto不能保留const属性
const int a = 100;
auto b = a;
b = 1000;
std::cout << b; //1000,b已经被改变,不是常量了

int a = 100;
int& la = a;
auto b = la; //b为int,去掉了引用属性
b=200;
std::cout<<a; // 这里a输出仍为100,证明b不是引用


auto        x = 10L;     // auto推导为long,x是long
auto&       x1 = x;      // auto推导为long,x1是long&
auto*       x2 = &x;     // auto推导为long,x2是long*
const auto& x3 = x;      // auto推导为long,x3是const long&
auto        x4 = &x3;    // auto推导为const long*,x4是const long*

因为auto会去掉引用属性,所以在声明函数的时候,可以使用拖尾函数指明auto推导的类型

int& bigger(int& a, int& b)
{
	return a > b ? a : b;
}
//拖尾函数
auto bigger(int& a, int& b)->int&
{
	return a > b ? a : b;
}
//因为函数返回引用类型,所以直接将大的那个数改为500,如果使用auto bigger(int& a, int& b)就不行
int a = 100;
int b = 50;
bigger(a, b) = 500;
std::cout << a << std::endl; // 500

decltype

因为auto必须需要表达式,而没有表达式的时候,就要用decltype,也就是自己把表达式带上

decltype 的形式很像函数,后面的圆括号里就是可用于计算类型的表达式(和 sizeof 有点类似),其他方面就和 auto 一样了,也能加上 const、*、& 来修饰。但因为它已经自带表达式,所以不需要变量后面再有表达式,也就是说可以直接声明变量。

int x = 0;          // 整型变量

decltype(x)     x1;        // 推导为int,x1是int
decltype(x)&    x2 = x;    // 推导为int,x2是int&,引用必须赋值
decltype(x)*    x3;        // 推导为int,x3是int*
decltype(&x)    x4;        // 推导为int*,x4是int*
decltype(&x)*   x5;        // 推导为int*,x5是int**
decltype(x2)    x6 = x2;   // 推导为int&,x6是int&,引用必须赋值

auto与decltype的区别:decltype 不仅能够推导出值类型,还能推导出引用类型,也能保留const属性,就是表达式的“原始类型”
除了加上 * 和 & 修饰,decltype 还可以直接从一个引用类型的变量推导出引用类型(上面的x6),而 auto 就会把引用去掉,推导出值类型。所以,完全可以把 decltype 看成是一个真正的类型名,用在变量声明、函数参数 / 返回值、模板参数等任何类型能出现的地方,只不过这个类型是在编译阶段通过表达式“计算”得到的。

decltype规则:

  • 如果表达式没经历运算,则推断类型和表达式完全一样(包括const,引用等)
  • 如果经历了运算,根据运算结果是否有固定地址(左值)来确定。有固定地址,推断为引用类型;没有则推断为运算结果类型
  • 表达式为函数,根据函数返回类型决定,但不会真的执行函数,而auto会去执行函数
int a = 100;
unsigned b = 50;
int* pa{ &a };
decltype(a+b) x; 		// x为unsigned,因为a+b是临时变量,没有固定地址(右值),a+b运算结果是unsigned
decltype(*pa) x=a; 		// x为int&,*pa运算结果有固定地址,推断为引用类型,引用类型必须赋初值
decltype(*pa + 1) x; 	//x为int,*pa+1为右值
decltype(pa[0]) x=a; 		//x为int&,pa[0]有固定地址,即使pa[2]也为int&,因为pa[2]也有固定地址,只是不能访问
decltype((a)) x=a;  		// x为int&,()也是一次运算
//证明上述第三点,结果只输出auto调用,证明decltype不会执行函数
int func1(int a)
{
	std::cout << "auto调用" << std::endl;
	return a;
}
int func2(int a)
{
	std::cout << "decltype调用" << std::endl;
	return a;
}

int main()
{
	auto autox = func1(100);
	decltype(func2(100)) declx;
	return 0;
}

C++14 增加了一个“decltype(auto)”的形式,既可以精确推导类型,又能像 auto 一样方便使用。C++14还新增了一个应用场合,就是使用auto能够推导函数返回值,这样在写复杂函数的时候,比如返回一个 pair、容器或者迭代器,就会很省事。

decltype 是 auto 的高级形式,更侧重于编译阶段的类型计算,所以常用在泛型编程里,获取各种类型,配合 typedef 或者 using 会更加方便。

//C++11的写法
//auto bigger(int& a, int& b)->decltype(a > b ? a : b)
//{
//	return a > b ? a : b;
//}

// C++14的写法
decltype(auto) bigger(int& a, int& b)
{
	return a > b ? a : b;
}
int main()
{
	int a = 100, b = 200;
	bigger(a, b)=400;
	std::cout << b; //400
	return 0;
}

注意的问题:

template<typename T1, typename T2>
decltype(auto) bigger(T1 &a, T2& b)
{
	return a > b ? a : b;
}
int main()
{
	float a = 50.2f;
	int b = 500;
	bigger(a, b);  //推断出的返回类型为float,注意不是float的引用
    
    // 函数在比较a和b大小的时候,做了隐式类型转换,将b转成了float,因此返回的是临时变量(右值),所以是值类型
    // 隐式类型转换产生了一个临时变量来存储转换后的值
     //例如:
    float& x = (float)b;  //此处会报错初始值必须是左值,说明隐式类型转换时是右值
}

以上就是auto和decltye常见用法

  • 3
    点赞
  • 8
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值