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; 正确。
}

C++ 自动类型推断的更多相关文章

  1. C++11新特性:自动类型推断和类型获取

    声明:本文是在Alex Allain的文章http://www.cprogramming.com/c++11/c++11-auto-decltype-return-value-after-functi ...

  2. Java 8新特性探究(三)泛型的目标类型推断

    简单理解泛型 泛型是Java SE 1.5的新特性,泛型的本质是参数化类型,也就是说所操作的数据类型被指定为一个参数.通俗点将就是"类型的变量".这种类型变量可以用在类.接口和方法 ...

  3. c++11 类型推断

    自动类型推断 当编译器能够在一个变量的声明时候就推断出它的类型,那么你就能够用auto关键字来作为他们的类型: auto x = 1; 编译器当然知道x是integer类型的.所以你就不用int了.接 ...

  4. C++11特性——变量部分(using类型别名、constexpr常量表达式、auto类型推断、nullptr空指针等)

    #include <iostream> using namespace std; int main() { using cullptr = const unsigned long long ...

  5. Swift语言指南(四)--类型安全和类型推断

    原文:Swift语言指南(四)--类型安全和类型推断 Swift是一门类型安全语言,类型安全语言需要代码里值的类型非常明确.如果你的代码中有部分值需要String类型,你就不能错误地传递Int. 鉴于 ...

  6. java 11 局部变量类型推断

    什么是局部变量类型推断? var javastack = "javastack"; System.out.println(javastack); 大家看出来了,局部变量类型推断就是 ...

  7. Java 10 实战第 1 篇:局部变量类型推断

    现在 Java 9 被遗弃了直接升级到了 Java 10,之前也发过 Java 10 新特性的文章,现在是开始实战 Java 10 的时候了. 今天要实战的是 Java 10 中最重要的特性:局部变量 ...

  8. java泛型-自定义泛型方法与类型推断总结

    下面是自定义泛型方法的练习: package com.mari.generic; import java.util.ArrayList; import java.util.Collection; im ...

  9. scala学习手记20 - 方法返回类型推断

    除了推演变量的类型,scala也会推演方法的返回类型.不过这里有一处需要注意:方法返回类型的推演依赖于方法的定义方式.如果用等号"="定义方法,scala就会推演方法返回类型:否则 ...

随机推荐

  1. $[NOIp2008]$双栈排序 栈/二分图/贪心

    \(Sol\) 先考虑单栈排序,怎么样的序列可以单栈排序呢?设\(a_i\)表示位置\(i\)是哪个数.\(\exist i<j<k\),都没有\(a_k<a_i<a_j\), ...

  2. CentOS防火墙iptables使用

    1.1 企业安全优化配置原则 尽可能不给服务器配置外网ip ,可以通过代理转发或者通过防火墙映射.并发不是特别大情况有外网ip,可以开启防火墙服务高并发的情况,不能开iptables,会影响性能,利用 ...

  3. 「Luogu P2015」二叉苹果树 解题报告

    题面 一个二叉树,边数为n\((2<n\le 100)\),每条边有一个权值,求剪枝后剩下p\((1<p<n)\)条边,使p条边的权值和最大 还看不懂?-- 2 5 input:5 ...

  4. yum回滚至Kubernetes1.15.7版本

    背景:升级Kubernetes版本从1.14.1-->1.17.0,官方说明不能跨版本升级,所以只好一个版本一个版本升级.最终升到1.17.0后发现API的格式有改动,之前的中间件版本很多不支持 ...

  5. codevs 3981 动态最大子段和(线段树)

    题目传送门:codevs 3981 动态最大子段和 题目描述 Description 题目还是简单一点好... 有n个数,a[1]到a[n]. 接下来q次查询,每次动态指定两个数l,r,求a[l]到a ...

  6. css3实现左右div高度自适应且内容居中对齐

    主要运用了css3的弹层布局,直接上代码: 效果:左边盒子宽度固定.内容居中对齐.与右侧盒子高度相等,右侧自动缩放 html: <div class="main"> & ...

  7. RedisCluster linux下批量删除 key

    Redis Cluster linux下批量删除键 说明 使用时不支持传入参数 , 如 redis_batch_del.sh , 因为在linux下 会自动将 * 解析为当前目录下所有文件名, 目前还 ...

  8. Spring学习记录5——数据库事务基础知识

    何为数据库事务 “一荣共荣,一损共损”这句话很能体现事务的思想,很多复杂的事务要分步进行,但它们组成了一个整体,要么整体生效,要么整体失效.这种思想反映到数据库上,就是多条SQL语句,要么全部成功,要 ...

  9. Java知识体系框架

    前言:自从出生,每个人都是一个学习者或探索者.永远保持一颗谦逊的心态,遵循一定的方法和规范,去学习和实践,永远记得走走停停,多回头看看自己走过的路,温故而知新,也能更好地指导未来的路怎么走(同样,本篇 ...

  10. vue学习笔记1:el 与 data

    一.vue介绍 vue是目前三大主流框架之一(React.Angular.Vue) vue特点: 易用 灵活 高效 vue官网:官网链接 二,知识点 vue实例选项: el 注:不能 让el直接管理h ...