C 语言

关注公众号 jb51net

关闭
首页 > 软件编程 > C 语言 > C++11类型推断

详解C++11中的类型推断

作者:Shawn-Summer

C++11中为了更好的支持泛型编程,提供了 auto和decltype两个关键词,目的就是提供编译阶段的自动类型推导,这篇文章主要介绍了C++11中的类型推断,需要的朋友可以参考下

C++11中的类型推断

C++11中为了更好的支持泛型编程,提供了 autodecltype两个关键词,目的就是提供编译阶段的自动类型推导。

1.auto关键词的新意义

在C++98中,auto是一个类型修饰符,用以显式声明自动变量(局部变量的),而在C++11中,这一用法已经弃用,现在auto用于声明变量。

1.1 auto声明变量

我们通过几个例子看一下,auto声明变量时的规则。

    int x;
    int *y;
    double foo();
    int& bar();
    auto *a=&x;//int *
    auto &b=x;//int &
    auto c=y;//int *
    auto *d=y;//int *
    auto e=bar();// int 
    auto &f=bar();//int &

1.如果使得auto声明的变量是引用类型,必须使用auto &.
2.如果变量本身是指针类型,则auto *auto是一样的,这里*变成冗余符号

    double foo();
    float * bar();
    const auto a=foo();//const double 
    const auto & b=foo();//const double &
    volatile auto  * const c=bar();//volatile float * const

    auto d=a;//double
    auto &e=a;//const double &
    auto f=c;//volatile float *
    volatile auto &g=c;//volatile float * &

3.声明为auto的变量不能从其初始化表达式中带走顶层cv限定符.

auto i=1,j=1.12f;//编译错误

4.auto可以声明多个变量,不过这些变量必须类型相同

1.2 auto无法推导的情况

#include<vector>
using namespace std;
void fun(auto x=1){}//错误
struct str
{
    auto var=10;//错误
};
int main()
{
    char x[3];
    auto y=x;
    auto z[3]=x;//错误
    vector<auto>V={1,2,3};//错误
}

auto不能作为函数形参(这是模板函数的事情)类中,auto不能用来声明非静态成员auto不能声明数组auto不能用于模板实参

2.decltype类型推导

decltypeauto都是用来类型推导的,不过 decltype的功能更加强大 ,下面就是decltype的简单用法

2.1 decltype的应用场景

一种情况是,decltypeauto一样来声明变量:

#include<typeinfo>
#include<iostream>
using namespace std;
int main()
{
    int i;
    decltype(i) j=0;
    cout<<typeid(j).name()<<endl;//i

    float a;
    double b;
    decltype(a+b) c;
    cout<<typeid(c).name()<<endl;//d
}

另一种情况是:typedefusing配合使用decltype

using  size_t=decltype(sizeof(0));
using  ptrdiff_t=decltype((int*)0-(int*)0);
using  nullptr_t=decltype(nullptr);

顺便一提,在C++11中,using已经完美替代typedef:

#include<iostream>
#include<type_traits>
#include<string>
#include<map>
using namespace std;

typedef unsigned int UINT;
using uint=unsigned int;

template<typename T>
using Mapstring=map<T,char*>;
Mapstring<int> number_string;

int main()
{
    cout<<is_same<uint,UINT>::value<<endl;//1
    cout<<is_same<map<int,char*>,decltype(number_string)>::value<<endl;//1
}

typedef能干的事情,using都能干,但是using能干的,比如给模板取一个别名,typedef做不到

decltype的另一种功能就是给匿名的结构体或者枚举推导类型来重用,

enum class {K1,K2,K3} anon_e;
decltype(anon_e) as;

2.2 decltypeauto更加精确

首先decltypeauto最明显的不同就是,decltype(e)它和sizeof()差不多,可以接收一个参数,不过这里我讲的不同是,同一个变量,使用decltypeauto得到的结果不同。
说直接点,decltype的类型推断比auto准确

const int ic=0;
decltype(ic) a;//const int
auto b=ic;//int

volatile int iv;
decltype(iv) c;//volatile int
auto d=iv;//int

struct S
{
    int i;
};
const S cs={0};
decltype(cs.i) e;//int

auto它不能带走变量的顶层cv限定符,而decltype(e)却可以带走ecv限定符,所以说,decltype的类型推断更加准确。还要一点细节,就是说类本身是用cv限定符修饰的,而类成员使用decltype时确推断不出来。

我们知道,auto只能带走指针类型,却无法带走引用类型,而decltype就可以同时带走引用和指针

#include<iostream>
#include<type_traits>
using namespace std;
int main()
{
    int i=1;
    decltype(i) & var1=i;// int &
    cout<<is_lvalue_reference<decltype(var1)>::value<<endl;//1

    int &j=i;
    decltype(j) var2=i;
    decltype(j)& var3=i;
    cout<<is_same<decltype(j),decltype(j)&>::value<<endl;//1,`&`的冗余

    int* p=&i;
    decltype(p) var4=&i;//int *
    decltype(p)* var5=&p;//int **

    const int k=1;
    const decltype(k) var6=1;//const int `const`冗余
}

上面这段代码,信息量很大
首先,decltype(e)可以带走e的引用和指针类型
其次,decltype(e)会对&cv限定符产生冗余,而不会对*产生冗余
最后,如果不确定decltype(e)的类型,可以使用<type_traits>头文件中的一些方法

总之,就是一句话:decltype(e)能直接返回e的准确类型
但是,如果decltype更加优越,那么为什么还要auto呢?

一种说法是auto用法更加简单,更重要的原因是,autolambda函数的配合使得,C++11相对于C++98,变得脱胎换骨,我个人认为C++11最重要的就是lambda函数。

2.3 decltype对于表达式的推断

我们知道在decltype(e)中,e被要求是一个表达式,即expression,而在上面我们所讲的e通常是一个名称,即id_expression,如果e是一个非名称的表达式,那么推断结果也会不同

int i;
decltype(i) a;//int
decltype((i)) b;//int &

在上面例子中,i就是一个id_expression,而(i)它不是id_expression,而是一个左值表达式,所以上述推导结果会不同。

我们直接来看decltype(e)的推导细则

int i=4;
int arr[5]={0};
int *ptr=arr;
struct S
{
    double d;
}s;
void foo(int);
void foo(double);
int && Rref();//函数返回值是将亡值
const bool Func(int);

decltype(arr) var1;//int[5]
decltype(ptr) var2;//int *
decltype(s.d) var3;//double
decltype(foo) var4;//无法通过编译,foo被重载

decltype(Rref()) var5;//int && 

decltype(true? i:i) var6;//int&
decltype((i)) var7;//int &
decltype(++i) var8;//int &
decltype(arr[3]) var9;// int &
decltype(*ptr) var10;//int &
decltype("abc") var11;//const char(&) [4]

decltype(1) var12;//int
decltype(i++) var13;//int
decltype(Func(1)) var14=true;//const bool

3.追踪返回类型

autodecltype可以进行配合使用,来实现泛型编程中的追踪返回类型

template<class T1,class T2>
decltype(t1+t2) sum(T1& t1,T2& t2)
{
    return (t1+t2);
}

上面这段代码,想法狠简单,但是它都无法通过C++11和C++98中的编译器,因为编译器是从左往右读的,读到decltype(t1+t2)时,t1t2没有声明,所以无法通过编译,我们可以通过返回类型后置的方法实现上述功能

template<typename T1,typename T2>
auto sum(T1& t1,T2& t2)->decltype(t1+t2)
{
    return (t1+t2);
}

上面就是追踪返回类型,最终sum函数的返回类型由decltype(t1+t2)确定,而不是auto确定,如果我们把->decltype(t1+t2)去掉,那么最终返回类型就由auto指定,我们其实很不希望这样,因为auto并不精确,decltype更加精确。

追踪返回类型其实就是返回类型后置,它的还有一种用法就是,提高函数指针的可读性:

#include<type_traits>
#include<iostream>
using namespace std;
int (*(*pf())())(){
    return nullptr;
}

auto pf1() ->auto (*)() -> int (*)()
{
    return nullptr;
}
int main()
{
    cout<<is_same<decltype(pf),decltype(pf1)>::value<<endl;//1
}

上述代码中,pfpf1都是函数指针,其返回的也是一个函数指针,该函数指针又返回一个函数指针,不过明显pf1的定义方式可读性更高。

到此这篇关于C++11中的类型推断的文章就介绍到这了,更多相关C++11类型推断内容请搜索脚本之家以前的文章或继续浏览下面的相关文章希望大家以后多多支持脚本之家!

您可能感兴趣的文章:
阅读全文