C++11之auto类型推导

简介: C++11之auto类型推导

静态类型、动态类型与类型推导

在聊auto类型推导前,我们先理解一下静态类型动态类型吧。

我想对于C/C++程序员而言,静态类型应该都不陌生,因为C/C++语言所有的变量必须遵循先定义后使用

而python语言就是典型的动态类型,先用即用

例如下面代码:

a = 22
    print("a:", a)


对于编译器/解释器来讲,静态类型是在编译时期进行类型检查,动态类型则是在运行时期对类型检查。

运行时期对类型检查这种功能我们就称之为类型推导

auto的历史变更

在早期的C++中,auto的功能是具有自动存储期的局部变量,但是因为声明变量时默认都是自动存储期的局部变量,所以auto就显得很鸡肋,为此在C++11标准委员会就将auto重新定义了。由一个存储类型指示符(storage-class-specifier,如 static、extern等)变成了新的类型指示符(type-specifier,如int、float等)。


注意:auto声明的变量类型必须由编译器在编译时期推导得出,这会导致通过auto定义的变量必须赋予初值。见下图

auto类型推导的用法还是蛮简单的,这里我写了一个简单的例子。

#include <iostream>
int main()
{
  auto a = 111;
  auto b = 1.f;
  auto c = a + b;
  return 0;
}


auto的优势

auto这个关键字在实际开发中使用还是蛮频繁的(指C++11的auto)。所以对于一个经常使用STL的程序员简直就是福音,因为这能极大的改善声明变量类型的长度,使之简单化。


auto-自动类型推导

例如下面这个,对于一个map进行迭代器遍历,我们定义迭代器变量的类型就需要是std::map<int, std::string>::iterator ,这绝对是所有程序员不想看见的,太冗余了。

#include <iostream>
#include <map>
#include <iterator>
#include <string>
int main()
{
  std::map<int, std::string> mm;
  mm.insert(std::make_pair(1, "张三"));
  mm.insert(std::make_pair(2, "李四"));
  for (std::map<int, std::string>::iterator iter = mm.begin(); iter != mm.end(); ++iter)
  {
    std::cout << iter->first << "\t" << iter->second << std::endl;
  }
  return 0;
}

那么我们就可以用auto来优化。 这样的代码又简洁又易读。

#include <iostream>
#include <map>
#include <iterator>
#include <string>
int main()
{
  std::map<int, std::string> mm;
  mm.insert(std::make_pair(1, "张三"));
  mm.insert(std::make_pair(2, "李四"));
  for (auto iter = mm.begin(); iter != mm.end(); ++iter)
  {
    std::cout << iter->first << "\t" << iter->second << std::endl;
  }
  return 0;
}


auto还可以用在类型计算上,编译器会为你计算出最为适合的类型

#include <iostream>
int main()
{
  int a = 3;
  auto b = a * 1.1f;
  auto c = a * 1.1;
  return 0;
}


auto也不是万能的,在对于极端的精度计算时,auto也无能为力。

#include <iostream>
int main()
{
  unsigned int a = UINT_MAX;
  unsigned int b = 1;
  auto c = a + b;
  std::cout << c << std::endl;  // 0
  return 0;
}


虽然a+b会导致数值溢出问题,但是a+b的返回值是unsigned int所以auto并不能帮你自动扩展。

auto最大的优点在于”自适应“可应用于模板中

在下面这个例子中,Sum函数接受类型T1、T2的俩个变量,模板实例化时才可以确定类型。所以在Sum方法中的s变量的类型随着我们对模板的实例化不同它的类型也会不同。

#include <iostream>
template<typename T1, typename T2>
double Sum(T1 t1, T2 t2)
{
  auto s = t1 + t2;
  return s;
}
int main()
{
  auto a = Sum<int, double>(1, 1.2);
  auto b = Sum<int, int>(2, 6);
  return 0;
}

模板实例化为Sum<int, double>时,s的变量类型为double

模板实例化为Sum<int, int>时,s的变量类型为int

auto在其他场景的使用与限制


auto可引用于其他场合

  1. 可用于初始化列表
  2. 可用于new关键字在堆区创建变量
#include <iostream>
int main()
{
  auto b{ 1 }; // 可用于初始化列表
  auto c = new auto(1); // 可用于new关键字在堆区创建变量
  return 0;
}


auto不能使用的场合

  1. 不能用于函数参数
  2. 不能用于非静态成员变量
  3. 不能用于定义数组
  4. 不能当作模板参数
#include <vector>
void Fun(auto x = 1) // 1:auto函数参数 无法通过编译
{
}
class Test
{
public:
  auto var = 10;  // 2:auto非静态成员变量 无法通过编译
};
int main()
{
  auto arr[3] = { 1,2,3 }; // 3:auto数组 无法通过编译
  std::vector<auto> arr2; // 4:auto模板参数 无法通过编译
  return 0;
}


对于函数 fun来说,auto不能是其形参类型。你可能感觉对于fun来说,由于其有默认参数,所以应该推导fun形参x的类型为int型。但事实却无法符合大家的想象。因为auto是不能做形参的类型的。如果程序员需要泛型的参数,还是需要求助于模板。

对于结构体/类来说,非静态成员变量的类型不能是 auto的。同样的,由于var定义了初始值,你可能认为auto可以推导str成员var的类型为int 的。但编译器阻止 auto对结构体/类中的非静态成员进行推导,即使成员拥有初始值。

声明auto数组。我们可以看到,main中的声明auto arr[3]这样的数组同样会被编译器禁止。

在实例化模板的时候使用auto作为模板参数,如main中我们声明的 vector<auto> v。你可能认为这里一眼而知是int类型,但编译器却阻止了编译。

以上4种情况的特点基本相似,我们其实可以很容易能够推导出auto所在位置应有的类型,但现有的C++11的标准还没有支持这样的使用方式。

目录
相关文章
|
1月前
|
存储 编译器 程序员
C++类型参数化
【10月更文挑战第1天】在 C++ 中,模板是实现类型参数化的主要工具,用于编写能处理多种数据类型的代码。模板分为函数模板和类模板。函数模板以 `template` 关键字定义,允许使用任意类型参数 `T`,并在调用时自动推导具体类型。类模板则定义泛型类,如动态数组,可在实例化时指定具体类型。模板还支持特化,为特定类型提供定制实现。模板在编译时实例化,需放置在头文件中以确保编译器可见。
31 11
|
1月前
|
存储 安全 编译器
【C++】C++特性揭秘:引用与内联函数 | auto关键字与for循环 | 指针空值(一)
【C++】C++特性揭秘:引用与内联函数 | auto关键字与for循环 | 指针空值
|
2月前
|
安全 程序员 编译器
C++ 11新特性之auto和decltype
C++ 11新特性之auto和decltype
38 3
|
1月前
|
存储 编译器 程序员
【C++】C++特性揭秘:引用与内联函数 | auto关键字与for循环 | 指针空值(二)
【C++】C++特性揭秘:引用与内联函数 | auto关键字与for循环 | 指针空值
|
2月前
|
安全 程序员 C语言
C++(四)类型强转
本文详细介绍了C++中的四种类型强制转换:`static_cast`、`reinterpret_cast`、`const_cast`和`dynamic_cast`。每种转换都有其特定用途和适用场景,如`static_cast`用于相关类型间的显式转换,`reinterpret_cast`用于低层内存布局操作,`const_cast`用于添加或移除`const`限定符,而`dynamic_cast`则用于运行时的类型检查和转换。通过具体示例展示了如何正确使用这四种转换操作符,帮助开发者更好地理解和掌握C++中的类型转换机制。
|
3月前
|
C++
使用 QML 类型系统注册 C++ 类型
使用 QML 类型系统注册 C++ 类型
56 0
|
3月前
|
存储 C++
【C/C++学习笔记】string 类型的输入操作符和 getline 函数分别如何处理空白字符
【C/C++学习笔记】string 类型的输入操作符和 getline 函数分别如何处理空白字符
40 0
|
3月前
|
设计模式 安全 IDE
C++从静态类型到单例模式
C++从静态类型到单例模式
36 0
|
3月前
|
存储 编译器 C++
【C++关键字】auto的使用(C++11)
【C++关键字】auto的使用(C++11)
|
4天前
|
存储 编译器 C++
【c++】类和对象(中)(构造函数、析构函数、拷贝构造、赋值重载)
本文深入探讨了C++类的默认成员函数,包括构造函数、析构函数、拷贝构造函数和赋值重载。构造函数用于对象的初始化,析构函数用于对象销毁时的资源清理,拷贝构造函数用于对象的拷贝,赋值重载用于已存在对象的赋值。文章详细介绍了每个函数的特点、使用方法及注意事项,并提供了代码示例。这些默认成员函数确保了资源的正确管理和对象状态的维护。
21 4