自动类型推导
在c++11中,关于类型推导的关键字有 using,decltype,auto,typeid。
在c++11之前我们要声明一个类型的别名往往使用的是typedef,但是该关键字在模板类型推导中存在一些缺陷,如下:
- 无法根据模板类型动态的进行类型推导(使用decltype解决)
- 无法对模板的实例进行typedef(使用using解决)
先看下面的示例代码:
#include <iostream>
#include <vector>
template<class Container>
class Foo
{
public:
void func(Container &c)
{
m_it = c.begin();
}
private:
typename Container::iterator m_it;
};
template <typename T>
class MapString
{
public:
typedef std::map<std::string,T> type;
};
上面的代码演示了在c++11之前使用typedef为容器迭代器取别名,以及实例化std::map。调用代码如下:
Foo<std::vector<int>> f;
std::vector<int> data{1,2,3};
const std::vector<int> const_data{1,2,2};
f.func(data); // 合法
f.func(const_data); // 非法,因为 m_it 是一个非 const 的迭代器,而 const_data 的迭代器是 const 版本的
// 使用 MapString
MapString<int>::type stringInt; // 使用起来很别扭,无法直接使用 MapString
要解决上面的问题,在c++11之前需要为Foo提供const版本的特化版本,而MapString却没有好的解决办法。在c++11中这两个都可以使用 using 来解决。可以说能使用 typedef 的地方一定可以使用 using,而能使用using的不一定可以使用typedef。
using:更好的typedef
using 作用与typedef一样,用于声明一个类型的别名,但是它的语法更自然,适用范围更广,使用它定义一个新的类型就像定义一个变量一样简单自然。
using 在模板别名中的使用
template <typename T>
using StringMap = std::map<std::string,T>;
// 使用 StringMap
StringMap<int> v;
using 在函数指针中的使用
在传统的使用typedef声明一个函数指针类型的时候语法往往晦涩难懂,而使用using语句声明函数指针将更加清晰明了,对比如下:
typedef int(*func_t)(char *src,size_t len);
using func_t = int(*)(char *,size_t);
decltype:类型推导
decltype可以让编译器推导出一个表达式的类型,它比我们常用的typeof更加智能。它的使用很像sizeof,只不过一个用来计算对象的字节数,一个用来推导表达式的类型。示例使用如下:
std::map<std::string,int> coll;
decltype(coll)::value_type elm; // <--- decltype(coll)::value_type的类型是std::pair<std::string,int>,因此,elm的类型就是一个std::pair
One application of decltype is to declare return types (see below). Another is to use it in metaprogramming(seeSection5.4.1,page125)or to pass the typeof alambda(seeSection10.3.4,page504).
使用decltype进行类型推导
文章开头介绍的Foo不得不为const的容器提供特化版本,如果使用decltype则可以减少程序员的重复工作,使用decltype版本的代码如下:
template <typename Containor>
class Goo
{
public:
void func(Containor &c)
{
m_it = c.begin();
}
private:
decltype(Containor().begin()) m_it;// 关键
};
int main(int argc,char **argv)
{
Goo<std::vector<int>> g;
Goo<const std::vector<int>> cg;
std::vector<int> data{1,2,3};
const std::vector<int> const_data{1,2,2};
g.func(data); // 合法
cg.func(const_data); // 合法,推导出的 m_it 类型为 const_iterator
return 0;
}
上面的关键代码为decltype那一行,它先定义一个匿名的Containor对象,然后获取其begin()迭代器,之后使用decltype推导出其容器的迭代器类型。
decltype的功能是在编译时推导出一个表达式的类型,其语法如下:
decltype(exp)
由于decltype接受的是一个表达式,这给类型推导很大的自由。
decltype 的推导规则
- 如果exp是类型、标识符,则推导的类型与exp一致
- 如果exp是函数调用,则其推导类型与函数返回类型一致
- 若exp是一个左值,则其推导类型是exp类型的左值引用;否则和exp类型一致
int &func_int_l();
int &&func_int_rr();
int func_int();
int x;
decltype(x) al = x;
// 《深入应用C++11》 page11
auto:新的意义
auto 示例
// AUTO的示例
auto x = 5; // x 是 int 类型
auto pi = new auto(x); //
const auto *v = &x,u = 6; // 这里的 u = 6 是一定要进行赋值的,否则u仍旧会报错,虽然前面经过推导auto 的类型为int。同时,如果写成u=6.0编译器也会报错,因为和前面auto推导出的类型冲突
/*AUTO的推导规则*/
int x = 0;
auto *a = &x; // a --> int *
auto b = &x; // b --> int *
auto &c = x; // c --> int &
auto d = c; // d --> int
const auto e = x; // e --> const int
auto f = e; // f --> int
const auto & g = x; // g --> const int &
auto & h = g; // h --> const int &
- auto 声明的变量必须立马初始化,可以把auto看成一个占位符,变量的类型由编译器在编译期确定
- auto 在类中只能修饰静态变量,不能修饰成员变量
- 如果使用auto在一条语句中定义多个变量,则这些变量的类型必须保持一致性
- 由f的推导,可知当表达式带有const的时候,auto会把const属性抛弃掉,推导成 none-const 类型
- 由 h 的推导可知,当 auto 和 引用(或者指针)结合时,auto的推导将保留 const 属性
auto 的限制
- auto 不能用于函数参数
- auto 不能用于类的非静态成员变量auto 无法定义数据
- auto 无法推导出模板类型,不可以作为模板的类型入,std::vector*<auto>* vec{1,2,3};
auto 的使用场景
auto 虽好,不要滥用!!!以下为auto建议的使用场景:
- 定义简单的类型,或者复杂的类型作为返回值时使用auto进行推导
- 遍历容器的时候使用auto替代迭代器
- 定义lambda表达式的时候
后置类型推导—— auto 和 decltype 在函数返回值中的应用
除了用来定义对象,decltype还可以在函数定义中使用,它可以通过一个表达式的类型来自动推导出函数的返回类型,如下:
template <typename T1, typename T2>标签:11,02,decltype,推导,int,auto,c++,类型,const From: https://blog.51cto.com/u_6650004/5915178
auto add(T1 x, T2 y) -> decltype(x+y)
{
return x + y;
}
template<class T>
T &foo(T &t)
{
return t + 1;
}
auto add(T &val) -> decltype(foo(val))
{
return foo(val);
}