C++ 类型推导:auto和decltype

类型推导:auto和decltype

auto

基本用法

使用它来做自动类型推导,可以和其他操作符(&,*,const),一般来说auto根据变量的初始值来推导出变量类型的,比如一些容器的迭代器完整写下来就很长,使用auto就很方便

1
2
3
4
5
6
7
int a = 5 , b = 3;
auto* pointer = &b;//和*一起用
cout<<*pointer<<endl;
auto& refer = a;//和&一起用
cout<<refer<<endl;
const auto ConstVar = b;//和const一起用
ConstVar = 2;//报错:ConstVar是read-only的

auto trick

  • =右边是一个引用类型时auto会自动把引用抛弃,推导出原始类型:这是符合直觉的,我们希望引用与否掌握在程序员手上,因此这种帮助了我们根据意志自由决定。
1
2
3
int x = 5;
int &ref = x;
auto test = ref;//tesr推导出的类型为test
  • 当类型不为引用时,auto 的推导结果将不保留表达式的 const属性;
  • 当类型为引用时,auto 的推导结果将保留表达式的 const 属性。

对于上边两条做出解释:

1.当类型不为引用时,auto 的推导结果将不保留表达式的 const属性;

1
2
const int x = 5;
auto y = x;//y是int类型,会抛弃const

2.当类型为引用时,auto 的推导结果将保留表达式的 const 属性。

1
2
3
4
int t = 4;
const int &a = t;
auto test1 = a;//test1推导出int类型,给程序员自由
auto &test2 = a;//test2推导出const int&类型,其中auto为const int

这么做的原因是为了安全,如果你的auto推出的是int,那么也就是说可以通过这个引用去修改一个const的变量,这是不合理的(编译器会禁止这样做)。因此为了合理性、安全性,推导出const int是最好的选择。

  • auto 不能在函数的参数中使用 (版本低于C++20)

如果为了减少代码重复,模板是一个更好的替代方法

注意:C++20已经允许auto在函数参数中使用了

1
2
3
4
5
6
7
8
9
10
11
auto fun(auto x,auto y)
{
return x+y;
}
int main()
{
string s1 = "123";
string s2 = "345";
cout<<fun(s1,s2)<<endl;
return 0;
}
  • auto 不能作用于类的非静态成员变量(也就是没有 static 关键字修饰的成员变量)中
1
2
3
4
5
6
7
8
9
10
11
12
13
14
class A
{
public:

auto x;//error: non-static data member declared with placeholder 'auto'|
auto y;
};

int main()
{
A a;
a.x = 2;
return 0;
}
  • auto 关键字不能定义数组 (char[]不行,用char*的可以)
1
2
3
auto a[4] = "12345";//error:'a' declared as array of 'auto'|
auto b[3] = {1,2,3};//error:'b' declared as array of 'auto'|
auto* c = "12345";//ok

Reference : https://stackoverflow.com/questions/7107606/why-cant-i-create-an-array-of-automatic-variables

虽然auto x[4] ={ ....}是一个错误的用法,但是auto x = {1,2,3,4}会推出x是一个std::initializer_list<int>类型。

  • auto 不能作用于模板参数
1
2
3
4
5
6
7
8
9
template <typename T>
class A{
//TODO:
};
int main(){
A<int> C1;
A<auto> C2 = C1; //错误
return 0;
}

decltype

基本用法

decltypeauto不同,他使用exp表达式进行类型推导 (decltype(10.8) x = 5.5 ; x被推导成了 double

值得一提的是,decltype并不会计算表达式,因此不用担心decltype(fun())或decltype(a+b+c+d+e+f...) 执行函数、表达式造成的耗费空间/时间。

decltype + 变量

当使用decltype(var)的形式时,decltype会直接返回变量的类型,包括const 和 &,这是一种完美的保留

1
2
3
4
const int a = 0;
const int& b = a;
decltype(a) x = 0;//x是const int类型
decltype(b) y = x;//y是const int& 类型

decltype和数组组合时:结果是一个数组

1
2
int a[5];
decltype(a) b; //等价于int b[5];

decltype和指针组合

1
2
3
int a = 5;
int* pa = &a;
decltype(pa) b = &a; //int* b

decltype+表达式

当使用decltype(expr)的形式时,decltype会返回表达式结果对应的类型。一个表达式不是左值就是右值,因此,decltype(expr)的结果根据expr的结果不同而不同: expr返回左值,得到该类型的左值引用;expr返回右值,得到该类型。

1
2
3
4
5
6
int i = 4;
decltype(i+4) a;//i+4是一个表达式,因此这是一个右值,得到该类型:int
int* p = &i;
decltype(*p) b;//*p返回对象的左值,*p是int类型,因此这是int&,即int的左值引用。
int a[3] = {1,2,3};
decltype(a[1]) c;//a[1]要看operator[]的实现,数组这里[]的返回值为T&,即返回的是左值引用,左值引用是一个左值,所以推断出c的类型为int&。

当一个变量作为表达式时,即decltype((var))会推断出左值引用“

decltype单独作用于对象,没有使用对象的表达式的属性,而是直接获得了变量的类型。要想获得变量作为表达式的类型,可以加一个括号:decltype((var))

1
2
int i = 5;
decltype((i)) di = i;//(i)得到的是p的左值,因此一个左值返回的是左值的类型:int&

decltype+函数:

decltype作用于函数名会得到函数类型,注意这里是函数名不是函数调用,函数调用返回的是一个变量,因此属于上面decltype+变量的部分。

C++中通过函数的返回值和形参列表,定义了一种名为函数类型的东西。它的作用主要是为了定义函数指针

比如:

1
2
3
4
5
6
7
8
9
10
11
12
13
using FunType = int(const int&,int);
int fun(const int& x,int y)
{
cout<<x<<" "<<y<<endl;
return x+y;
};

int main()
{
FunType *pf = fun;
pf(2,3);
return 0;
}

我们可以从过decltype获得fun的类型:

1
decltype(fun) *pf2 = fun;//即FunType *pf = fun;

学会查看推导结果

  • 第一种方法就是通过IDE来查看

  • 第二种通过编译器报错来查看:

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    using FunType = int(const int&,int);
    int fun(const int& x,int y)
    {
    cout<<x<<" "<<y<<endl;
    return x+y;
    };

    template<typename T>
    class TD;

    int main()
    {
    TD<decltype(fun)>xtype;
    return 0;
    }

    报错:error: aggregate 'TD<int(const int&, int)> xtype' has incomplete type and cannot be defined|,可以得知:funint(const int&, int)类型。