C++11初探:类型推导,auto和decltype

2024-05-07 00:58

本文主要是介绍C++11初探:类型推导,auto和decltype,希望对大家解决编程问题提供一定的参考价值,需要的开发者们随着小编来一起学习吧!

这篇文章转载自http://www.cnblogs.com/npbool/p/3433360.html。

类型推导可以说是C++模拟动态语言特性的起点,就从这里开始这个系列吧。

auto

使用迭代器的时候,类型总是一件烦心的事。

vector<vector<int> > v;
vector<vector<int> >::iterator it = v.begin(); 

函数指针也同样, 类型声明很蛋疼:

复制代码
int add(int x,int y){return x+y;
}int main(){int (*func)(int,int) = add;cout<<func(1,2)<<endl;
}
复制代码

我既然把v.begin()赋给it, 类型已经在编译期确定了,编译器知道正确的类型是什么,再加一个类型声明实在很繁琐。C++11 有了auto。我们可以这样写:

vector<vector<int>> v; // C++11 可以不用在'>>'之间加空格了!
auto it = v.begin(); auto func = add;

编译器会根据值的类型,推导出autob变量。类型的推导是在编译期就完成的,仍是静态类型,和脚本语言不同。实际上是一个语法糖。但由于C++对模板的大量使用,一个变量的类型有时过于复杂难以写出,这样的语法糖是必要的。

很简单,不是么?(实际会有一些细节问题,文末再说)

decltype

如果我们只是想用一个编译器推导的类型声明一个变量,但不初始化,auto无能为力。此时可以用decltype。

复制代码
int add(int x,int y){return x+y;
}
int main(){double i=0;decltype(i) a; // doubledecltype(add()) b; //int 注意括号。不带括号就是函数指针了。
}
复制代码

需要说明,decltype仍然是编译期行为,add()函数不会真正执行,只是利用它推导了类型。

返回类型后置

考虑一个模板函数

template<typename U, typename V>
??? foo(U u, V v){...return u*v;
}

总之我要在这个函数做点事情,最后返回一个u*v类型的东西。返回类型该怎么写?

有了decltype,似乎可以这么:

template<typename U, typename V>
decltype(u*v) foo(U u, V v){ //WRONG!return u*v;
}

但是不行!u,v不在作用域。

u,v不能用,自己搞出一个可以用的U,V类型的变量总行了吧XD

template<typename U, typename V>
decltype(U(0)*V(0)) foo(U u, V v){return u*v;
}

试一下,foo(2,3.3)返回6.6,貌似正常。但是!你怎么知道U,V可以接受一个0作为构造参数的?U,V不一定是数值,有可能是重载了'*'操作符的奇怪东西。没关系,指针总是可以这样初始化的:

template<typename U, typename V>
decltype(*(U*)(0)**(V*)(0)) foo(U u, V v){return u*v;
}

但是你不觉得太丑了么。。。

C++11引入了返回类型后置解决这个问题,

template<typename U, typename V>
auto foo(U u, V v) -> decltype(u*v){return u*v;
}

语法很清晰。总之就是,返回类型后置以后,就进入函数的作用域了,参数都能用了!

另外还有一个用处,假设你要写个类

复制代码
//A.h
struct A {struct B {};B func();
};//A.cpp
A::B A::func(){return B();
}
复制代码

那个A::B怎么看都别扭,用返回类型后置,可以这样:

auto A::func() -> B {return B();
}

再来一次,后置的返回类型,处在函数作用域里,B已经可见,不需要A::修饰了。

但你肯定有一个问题,既然我函数代码已经给出了,编译器完全可以推断出返回类型是什么,为什么还要自己显式给出返回类型?如果能这样就好了:

template<typename U, typename V>
auto foo(U u, V v){return u*v;
}

但是很遗憾,不行。由于“复杂性”,标准没有这样做。这也容易理解,假如你的程序编译成了库,只给了别人头文件里的函数签名,函数体对编译器是不可见的,那你这个auto算什么意思?

但事情也不那么绝对,通过匿名函数返回值类型可省略这个特性加上auto,有些情况下我们的确可以不用写返回类型。以后再谈。

一些细节

虽然我很不想写细节,但C++的东西总不是那么简单,为了完整性,把这些黑暗角落放到最后,希望不会影响对以上特性的好感...

  • auto+引用
int i = 0;
int& r = i;
auto a = r; // type of a?
a = 10;
cout<<i<<endl; // value of i?

直观上感觉,r是i的引用,a自然也会推导出int&这个类型。但实际上,r只是i的别名,a类型的推导结果还是int。于是,对a的修改和i无关,结果输出0。如果想推导出引用,需要这样

auto& a = r; // auto& a = i也可以
  • auto+const

还有一点,auto会忽略顶层的const。

const int i = 0;
auto a = i; // a: int
audo b = &i // b: const int*

其中,a是普通的int, 因为i是const int,auto把顶层的const忽略了。b是const int*, 这个可以这样看,const int*实际上是“(const int)*”(当然代码不能这样写),const不是顶层修饰符了,就没有忽略。

如果想得到推导出一个const类型,需要

const auto a = i; // a: const int

另外,decltype和auto不同,它不会忽略引用和顶层const修饰。

const int i=0;
int j=0, &r = j;
decltype(i) a; //const int
decltype(r) b; //int& 

最后,比较特别的是,如果decltype里面的表达式被包含在括号中,视为对表达式求值的类型。

decltype((i)) a;//error: a is int& and must be initialized

对i求值返回的是int&。


这篇关于C++11初探:类型推导,auto和decltype的文章就介绍到这儿,希望我们推荐的文章对编程师们有所帮助!



http://www.chinasem.cn/article/965881

相关文章

C++使用栈实现括号匹配的代码详解

《C++使用栈实现括号匹配的代码详解》在编程中,括号匹配是一个常见问题,尤其是在处理数学表达式、编译器解析等任务时,栈是一种非常适合处理此类问题的数据结构,能够精确地管理括号的匹配问题,本文将通过C+... 目录引言问题描述代码讲解代码解析栈的状态表示测试总结引言在编程中,括号匹配是一个常见问题,尤其是在

使用C++实现链表元素的反转

《使用C++实现链表元素的反转》反转链表是链表操作中一个经典的问题,也是面试中常见的考题,本文将从思路到实现一步步地讲解如何实现链表的反转,帮助初学者理解这一操作,我们将使用C++代码演示具体实现,同... 目录问题定义思路分析代码实现带头节点的链表代码讲解其他实现方式时间和空间复杂度分析总结问题定义给定

C++初始化数组的几种常见方法(简单易懂)

《C++初始化数组的几种常见方法(简单易懂)》本文介绍了C++中数组的初始化方法,包括一维数组和二维数组的初始化,以及用new动态初始化数组,在C++11及以上版本中,还提供了使用std::array... 目录1、初始化一维数组1.1、使用列表初始化(推荐方式)1.2、初始化部分列表1.3、使用std::

C++ Primer 多维数组的使用

《C++Primer多维数组的使用》本文主要介绍了多维数组在C++语言中的定义、初始化、下标引用以及使用范围for语句处理多维数组的方法,具有一定的参考价值,感兴趣的可以了解一下... 目录多维数组多维数组的初始化多维数组的下标引用使用范围for语句处理多维数组指针和多维数组多维数组严格来说,C++语言没

Python如何计算两个不同类型列表的相似度

《Python如何计算两个不同类型列表的相似度》在编程中,经常需要比较两个列表的相似度,尤其是当这两个列表包含不同类型的元素时,下面小编就来讲讲如何使用Python计算两个不同类型列表的相似度吧... 目录摘要引言数字类型相似度欧几里得距离曼哈顿距离字符串类型相似度Levenshtein距离Jaccard相

Go语言中三种容器类型的数据结构详解

《Go语言中三种容器类型的数据结构详解》在Go语言中,有三种主要的容器类型用于存储和操作集合数据:本文主要介绍三者的使用与区别,感兴趣的小伙伴可以跟随小编一起学习一下... 目录基本概念1. 数组(Array)2. 切片(Slice)3. 映射(Map)对比总结注意事项基本概念在 Go 语言中,有三种主要

c++中std::placeholders的使用方法

《c++中std::placeholders的使用方法》std::placeholders是C++标准库中的一个工具,用于在函数对象绑定时创建占位符,本文就来详细的介绍一下,具有一定的参考价值,感兴... 目录1. 基本概念2. 使用场景3. 示例示例 1:部分参数绑定示例 2:参数重排序4. 注意事项5.

使用C++将处理后的信号保存为PNG和TIFF格式

《使用C++将处理后的信号保存为PNG和TIFF格式》在信号处理领域,我们常常需要将处理结果以图像的形式保存下来,方便后续分析和展示,C++提供了多种库来处理图像数据,本文将介绍如何使用stb_ima... 目录1. PNG格式保存使用stb_imagephp_write库1.1 安装和包含库1.2 代码解

C++实现封装的顺序表的操作与实践

《C++实现封装的顺序表的操作与实践》在程序设计中,顺序表是一种常见的线性数据结构,通常用于存储具有固定顺序的元素,与链表不同,顺序表中的元素是连续存储的,因此访问速度较快,但插入和删除操作的效率可能... 目录一、顺序表的基本概念二、顺序表类的设计1. 顺序表类的成员变量2. 构造函数和析构函数三、顺序表

使用C++实现单链表的操作与实践

《使用C++实现单链表的操作与实践》在程序设计中,链表是一种常见的数据结构,特别是在动态数据管理、频繁插入和删除元素的场景中,链表相比于数组,具有更高的灵活性和高效性,尤其是在需要频繁修改数据结构的应... 目录一、单链表的基本概念二、单链表类的设计1. 节点的定义2. 链表的类定义三、单链表的操作实现四、