C++ 自动类型推断
C++语言提供了自动类型推断的机制,用于简化代码书写,这是一种很不错的特性,使用auto和decltype都可以完成自动类型推断的工作,而且都工作在编译期,这表示在运行时不会有任何的性能损耗。
一、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++ 自动类型推断的更多相关文章
- C++11新特性:自动类型推断和类型获取
声明:本文是在Alex Allain的文章http://www.cprogramming.com/c++11/c++11-auto-decltype-return-value-after-functi ...
- Java 8新特性探究(三)泛型的目标类型推断
简单理解泛型 泛型是Java SE 1.5的新特性,泛型的本质是参数化类型,也就是说所操作的数据类型被指定为一个参数.通俗点将就是"类型的变量".这种类型变量可以用在类.接口和方法 ...
- c++11 类型推断
自动类型推断 当编译器能够在一个变量的声明时候就推断出它的类型,那么你就能够用auto关键字来作为他们的类型: auto x = 1; 编译器当然知道x是integer类型的.所以你就不用int了.接 ...
- C++11特性——变量部分(using类型别名、constexpr常量表达式、auto类型推断、nullptr空指针等)
#include <iostream> using namespace std; int main() { using cullptr = const unsigned long long ...
- Swift语言指南(四)--类型安全和类型推断
原文:Swift语言指南(四)--类型安全和类型推断 Swift是一门类型安全语言,类型安全语言需要代码里值的类型非常明确.如果你的代码中有部分值需要String类型,你就不能错误地传递Int. 鉴于 ...
- java 11 局部变量类型推断
什么是局部变量类型推断? var javastack = "javastack"; System.out.println(javastack); 大家看出来了,局部变量类型推断就是 ...
- Java 10 实战第 1 篇:局部变量类型推断
现在 Java 9 被遗弃了直接升级到了 Java 10,之前也发过 Java 10 新特性的文章,现在是开始实战 Java 10 的时候了. 今天要实战的是 Java 10 中最重要的特性:局部变量 ...
- java泛型-自定义泛型方法与类型推断总结
下面是自定义泛型方法的练习: package com.mari.generic; import java.util.ArrayList; import java.util.Collection; im ...
- scala学习手记20 - 方法返回类型推断
除了推演变量的类型,scala也会推演方法的返回类型.不过这里有一处需要注意:方法返回类型的推演依赖于方法的定义方式.如果用等号"="定义方法,scala就会推演方法返回类型:否则 ...
随机推荐
- Mysql 表与表之间的关系
一.前言 二.表与表之间的关系 (一) 一对多 (二) 一对一 (三) 多对多 一.前言 研究表与表之间的关系前,先要知道将所有数据存放在一张表中的弊端: 1.结构不清晰 ---> 不致命 2. ...
- 开发工具篇:JAVA和IntelliJ IDEA相恋
开发工具篇:JAVA和IntelliJ IDEA相恋 idea是什么? IDEA 全称 IntelliJ IDEA,是java语言开发的集成环境,IntelliJ在业界被公认为最好的java开发工具之 ...
- 处理样本不平衡的LOSS—Focal Loss
0 前言 Focal Loss是为了处理样本不平衡问题而提出的,经时间验证,在多种任务上,效果还是不错的.在理解Focal Loss前,需要先深刻理一下交叉熵损失,和带权重的交叉熵损失.然后我们从样本 ...
- 1082 射击比赛 (20 分)C语言
本题目给出的射击比赛的规则非常简单,谁打的弹洞距离靶心最近,谁就是冠军:谁差得最远,谁就是菜鸟.本题给出一系列弹洞的平面坐标(x,y),请你编写程序找出冠军和菜鸟.我们假设靶心在原点(0,0). 输入 ...
- yum回滚至Kubernetes1.15.7版本
背景:升级Kubernetes版本从1.14.1-->1.17.0,官方说明不能跨版本升级,所以只好一个版本一个版本升级.最终升到1.17.0后发现API的格式有改动,之前的中间件版本很多不支持 ...
- kafka sasl/plain安全认证
1.SASL认证机制版本支持 SASL/GSSAPI (Kerberos) - starting at version 0.9.0.0SASL/PLAIN - starting at version ...
- MongoDB 上手开发实践(入门上手开发这一篇就够了)
前言 MongoDB是一个介于 关系数据库 和非关系数据库之间的产品,是非关系数据库当中功能最丰富,最像关系数据库的.它支持的数据结构非常松散,是类似 json 的 bson 格式,因此可以存储比较复 ...
- beetlex网关之聚合和url请求过虑
在这里主要介绍beetlex应用网关的两个插件,分别是聚合和url请求过虑.通过聚合插件可以把整合多个请求的数据来应答请求端,而Url请求过虑同可以拒绝一些有非常关键字的请求. 请求聚合 在网关服务中 ...
- python基础操作以及变量运用
今天学习关于pycharm的操作以及变量的知识 1.关于pycharm的基本操作,作为一个小白,仪式感还是要有 在基础界面上新建然后打印hello world,也是对python的一种尊重吧 2.关于 ...
- js六种数据类型
六种数据类型: undefined . boolean .string .number .object .function 效果地址:https://scrimba.com/c/cEedDGTd 代 ...