C++语言提供了自动类型推断的机制,用于简化代码书写,这是一种很不错的特性,使用autodecltype都可以完成自动类型推断的工作,而且都工作在编译期,这表示在运行时不会有任何的性能损耗。

一、auto自动类型推断

auto自动类型推断的机制和函数模板的推断机制很相似,auto就类似于模板中的T

(1.1) auto变量以传值方式初始化

一句话总结:抛弃掉对象的const和引用属性,新对象就像一个全新的副本;对于指针,会抛弃其顶层const属性,保留其底层const属性。

int main(int argc, char *argv[])
{
int a = 0;
int &a_ref = a;
const int ca = 0;
const int &ca_ref = ca;
const int *const pa = &a;
int arr[] = {1, 2, 3};
const carr[] = {1, 2, 3}; // 一、传值方式。
auto b1 = a; // b1为int, auto为int
auto b2 = a_ref; // b2为int, auto为int
auto b3 = ca; // b3为int, auto为int
auto b4 = ca_ref; // b4为int, auto为int
auto b5 = pa; // b5为const int *, auto为const int *
auto b6 = arr; // b6为int*, auto为int*
auto b7 = carr; // b7为const int*, auto为const int*
auto b8 = main; // b8为int(*)(int, char**), auto为int(*)(int,char**)
return 0;
}

(1.2) auto变量的指针或者引用类型初始化

一句话总结:引用属性被抛弃,但是const属性会被保留;对于指针类型,所有的const属性都被保留。

int main(int argc, char *argv[])
{
int a = 0;
int &a_ref = a;
const int ca = 0;
const int &ca_ref = ca;
const int *const pa = &a;
int arr[] = {1, 2, 3};
const int carr[] = {1, 2, 3}; // 二、传引用或指针方式。
auto &b1 = a; // b1为int&, auto为int
auto &b2 = a_ref; // b2为int&, auto为int
auto &b3 = ca; // b3为const int&, auto为const int
auto &b4 = ca_ref; // b4为const int&, auto为const int
auto &b5 = pa; // b5为const int *const &, auto为const int *const
auto &b6 = arr; // b6为int*&, auto为int*
auto &b7 = carr; // b7为const int *&, auto为const int *
auto &b8 = main; // b8为int(&)(int, char**), auto为int(int,char**)
return 0;
}

(1.3) auto变量的万能引用初始化

见万能引用即可:C++引用详解

二、decltype自动类型推断

decltype相对于auto来讲更加温和,因为它不会丢弃任何的一个属性(不像auto一样可能丢弃常量和引用)。当我们不想用表达式的值来初始化变量,而仅希望推断表达式的数据类型时,就可以使用decltype

(2.1) decltype推断变量的类型

decltype推断的方式非常简单:原来的类型是什么,推断的结果就是什么

int main(int argc, char *argv[])
{
int a = 0;
int &ra = a;
const int &cra = a;
int *pa = &a;
const int *cpa = pa;
const int *const ccpa = pa; decltype(a) b1 = a; // b1为int
decltype(ra) b2 = a; // b2为int&
decltype(cra) b3 = a;// b3为const int&
decltype(pa) b4 = nullptr; // b4为int*
decltype(cpa) b5 = nullptr; // b5为const int *
decltype(ccpa) b6 = nullptr; // b6为const int *const
return 0;
}

(2.2) decltype推断表达式的类型

与推断变量不同的是,decltype表达式中的内容如果是左值,那么将推断为引用类型

int main(int argc, char *argv[])
{
int a = 0;
int *pa = &a; decltype(1 + 2) b1; // 推断为int。
decltype(*pa) b2 = a; // 由于*pa是左值,因此b的类型为int&。
decltype((a)) b3 = a; // 由于(a)是表达式,且是左值,因此b的类型为int&。
return 0;
}

(2.3) decltype推断函数或函数的调用结果

个人认为这是decltype最强大的地方,因为此时并不会实际地调用函数,而仅仅会推断类型(和sizeof十分相似):

#include <string>

std::string func() { return std::string(); }

int main(int argc, char *argv[])
{
decltype(func) a; // a为函数声明:std::string();
decltype(func) *b = nullptr; // b为函数指针:std::string(*)();
decltype(func) &c = func; // c为函数引用:std::string(&)();
decltype(func().c_str()) d = nullptr; // d为const char *
// 不会实际调用func()和c_str()。
return 0;
}

(2.4) decltype的特殊用法示例

(2.4.1) 利用decltype来提高变量声明的代码兼容性

利用decltype可以在模板编程中实现数据类型的动态效果:

template<typename T>
class container_copier
{
public:
typename T::iterator _begin;
typename T::iterator _end; container_copier(typename T::iterator begin, typename T::iterator end)
: _begin(begin), _end(end) {} void copy() { /* ... */ }
};

这个模板可以应付普通的容器类型,但是无法针对常量容器对象进行操作:

#include <vector>

int main(int argc, char *argv[])
{
std::vector<int> vec = {1, 2, 3, 4, 5};
container_copier<std::vector<int>> cpr1(vec.begin(), vec.end());
cpr1.copy(); // OK. const std::vector<int> cvec = {1, 2, 3, 4, 5};
container_copier<const std::vector<int>> cpr2(vec.begin(), vec.end()); // 错误 return 0;
}

错误的原因是:对于常量容器,begin函数返回的是const_iterator,而不是iterator。如果不适用自动类型推断,我们就不得不进行偏特化:

template<typename T>
class container_copier<const T>
{
public:
typename T::const_iterator _begin;
typename T::const_iterator _end; container_copier(typename T::const_iterator begin, typename T::const_iterator end)
: _begin(begin), _end(end) {} void copy() { /* ... */ }
};

只有这样才能够通过编译。这样重复地编写偏特化代码无疑是很麻烦的。但是使用了自动类型推断之后,事情就简单多了:

#include <vector>

template<typename T>
class container_copier
{
public:
decltype(T().begin()) _begin;
decltype(T().end()) _end; container_copier(decltype(T().begin()) begin,
decltype(T().end()) end)
: _begin(begin), _end(end) {} void copy() { /* ... */ }
}; int main(int argc, char *argv[])
{
std::vector<int> vec = {1, 2, 3, 4, 5};
container_copier<std::vector<int>> cpr1(vec.begin(), vec.end());
cpr1.copy(); // OK. const std::vector<int> cvec = {1, 2, 3, 4, 5};
container_copier<const std::vector<int>> cpr2(vec.begin(), vec.end()); // OK. return 0;
}

这样,通过自动类型推断,就可以由编译器来动态地决定究竟是使用iterator还是const_iterator

(2.4.2) 利用decltype来提高函数返回类型的兼容性

有时,我们希望根据函数入参的某个方法的返回类型来决定函数的返回类型,那么就可以利用自动类型推断:

template<typename T>
auto func(T&& t) -> decltype(t.foo())
{
// ...
return t.foo();
}

这样,就可以不显示地指定返回类型,而让返回类型随着foo()方法的类型而变化。

三、decltype(auto)类型推断(c++14)

decltype(auto)的语义是:要求编译器自动推断需要自动推断的类型。使用decltype(auto)的主要目的是:由于auto会抛弃掉引用或是常量属性,但是有时却需要自动类型推断来保留这些属性,因此decltype可以将auto丢弃的属性给找回来

int main(int argc, char *argv[])
{
int a = 0;
const int &a_ref = a;
auto b1 = a_ref; // b1为int
// 如果我们想保留b1怎么办呢?
// 方法一:
decltype(auto) b2 = a_ref; // 等价于:decltype(a_ref) b2 = a_ref;
// b2为const int &
// 方法二:
auto &b3 = a_ref;
return 0;
}

一个常见的坑是:当decltype推断左值表达式时,会产生引用类型;然而如果返回一个局部变量的引用将是危险的:

decltype(auto) func()
{
int i = 30;
// ...
return(i); // 错误,因为这等价于:decltype((i)), 这将返回i的引用,引发错误。
// return i; 正确。
}

最新文章

  1. AngularJS 中的Promise --- $q服务详解
  2. Windows cmd命令搜索顺序
  3. JDBC 基本操作
  4. 用纯原生js实现jquery的ready函数(两种实现)
  5. centos6.x已经安装的系统添加图形界面
  6. PS之火焰铁锈字
  7. AI自动寻路
  8. Android studio教程:[1] 创建app项目
  9. weiphp 微信公众号用程序来设置指定内容消息回复业务逻辑操作
  10. 使用AngularJS的三个重要原因
  11. shell脚本基础1 概述及变量
  12. Helicute FPV App Privacy Policy
  13. SSM框架应用
  14. DBCHART读取X、Y、LABEL值
  15. pyqt5-对文本样式进行操作
  16. matlab与示波器连接及电脑连接
  17. XMind使用教程
  18. webstorm “Unterminated statement”
  19. [转]PostgreSQL教程(十六):系统视图详解
  20. 函数的应用 &quot;注册&quot; and &quot;登录&quot;

热门文章

  1. CSP201903-2二十四点
  2. 洛谷$P$4137 $Rmq\ Problem / mex$ 主席树
  3. The server time zone value &#39;�й���׼ʱ��&#39; is unrecognized or represents more than one time zone错误的解决办法【已解决】
  4. RHEL6.6安装Oracle 11g RAC - 基于VMware的实验环境
  5. JS 中检测数组的四种方法
  6. PTC热敏电阻的应用
  7. 简述ASP.NET Web网页的工作原理。
  8. 图解kubernetes调度器ScheduleAlgorithm核心实现学习框架设计
  9. 小白学 Python 爬虫(41):爬虫框架 Scrapy 入门基础(八)对接 Splash 实战
  10. Django3.0.2学习踩坑记