C++ 的 decltype 详细介绍

1. 基本介绍

decltype 是 C++11 新增的一个用来推导表达式类型的关键字。和 auto 的功能一样,用来在 编译时期 进行自动类型推导。引入 decltype 是因为 auto 并不适用于所有的自动类型推导场景,在某些特殊情况下 auto 用起来很不方便,甚至压根无法使用。也可以将 decltype 看作是 sizeof 运算符的另一种形式,因为两者都不会真正计算其参数,只充当一种编译期工具的角色。

auto varName = value;
decltype(exp) varName = value;
  • auto 根据 = 右边的初始值推导出变量的类型,decltype 根据 exp 表达式推导出变量的类型,跟 = 右边的 value 没有关系;
  • auto 要求变量必须初始化,因为 auto 是根据变量的初始值来推导变量类型的,如果不初始化,变量的类型也就无法推导;
  • decltype 不要求,因此可以写成如下形式:
decltype(exp) varName;

原则上讲,exp 只是一个普通的表达式,它可以是任意复杂的形式,但必须保证 exp 的结果是有类型的,不能是 void;如果 exp 为一个返回值为 void 的函数时,exp 的结果也是 void 类型,此时会导致编译错误。

2. decltype 的几种形式

int x = 0;
decltype(x) y = 1;            // y -> int
decltype(x + y) z = 0;        // z -> int
const int& i = x;
decltype(i) j = y;            // j -> const int&
const decltype(z) *p = &z;    // *p -> const int, p -> const int*
decltype(z) *m = &z;          // *m -> int, m -> int*
decltype(m)* n = &m;          // *n -> int*, n -> int**

3. 推导规则

decltype 的推导规则可以简单概述如下:

  1. 如果 exp 是一个不被括号()包围的表达式,或者是一个类成员访问表达式,或者是一个单独的变量,decltype(exp) 的类型和 exp 一致。

代码示例:

#include<string> 
#include<iostream>
using namespace std;
 
class A {
    
    
public:
    static int total;
    string name;
    int age;
    float scores;
}
 
int A::total = 0;
 
int main()
{
    
    
	int n = 0;
	const int &r = n;
	A a;
	decltype(n) x = n;           // n 为 int,x 被推导为 int
	decltype(r) y = n;           // r 为 const int &,y 被推导为 const int &
	decltype(A::total)  z = 0;   // total 是类 A 的一个 int 类型的成员变量,z 被推导为 int
	decltype(A.name) url = "www.baidu.com"; // url 为 string 类型
	
	return 0;
}
  1. 如果 exp 是函数调用,则 decltype(exp) 的类型就和函数返回值的类型一致。

代码示例:

int& func1(int, char);   // 函数返回值为 int&
int&& func2(void);       // 函数返回值为 int&&
int func3(double);       // 函数返回值为 int
 
const int& func4(int, int, int);  // 函数返回值为 const int&
const int&& func5(void);          // 函数返回值为 const int&&
 
int n = 50;
decltype(func1(100,'A')) a = n; // a 的类型为 int&
decltype(func2()) b = 0;        // b 的类型为 int&&
decltype(func3(10.5)) c = 0;    // c 的类型为 int
 
decltype(func4(1,2,3)) x = n;    // x 的类型为 const int&
decltype(func5()) y = 0;         // y 的类型为 const int&&

exp 中调用函数时需要带上括号和参数,但这仅仅是形式,并不会真的去执行函数代码。

  1. 如果 exp 是一个左值,或被括号()包围,decltype(exp) 的类型就是 exp 的引用,假设 exp 的类型为 T,则 decltype(exp) 的类型为 T&

代码示例:

class A 
{
    
    
public:
   int x;
}
 
int main()
{
    
    
	const A obj;
	decltype(obj.x) a = 0;   // a 的类型为 int
	decltype((obj.x)) b = a; // b 的类型为 int&
	 
	int n = 0, m = 0;
	decltype(m + n) c = 0;     // n + m 得到一个右值,c 的类型为 int
	decltype(n = n + m) d = c; // n = n + m 得到一个左值,d 的类型为 int &
	return 0;
}

左值:表达式执行结束后依然存在的数据,即持久性数据;右值是指那些在表达式执行结束不再存在的数据,即临时性数据。一个区分的简单方法是:对表达式取地址,如果编译器不报错就是左值,否则为右值。

  1. 类的静态成员可以使用 auto, 对于类的非静态成员无法使用 auto,如果想推导类的非静态成员的类型,只能使用 decltype

代码示例:

template<typename T>
class A
{
    
    
private :
   decltype(T.begin()) m_it;
   // typename T::iterator m_it;   // 这种用法会出错
public:
	void func(T& container)
	{
    
    
	   m_it = container.begin();
	}
};
 
int main()
{
    
    
	const vector<int> v;
	A<const vector<int>> obj;
	obj.func(v);
	return 0;
}

4. 其它

此外,可以在函数模板、类模板和 lambda 表达式中使用 decltype,从而推断类型或者声明类型。

下面给出的示例代码展示了如何在 lambda 表达式中使用 decltype

#include <iostream>

int main()
{
    
    
    int x = 42;
    auto f = [&](decltype(x)& val) {
    
     val += 1; };
    f(x);

    std::cout << "x: " << x << std::endl;  // x: 43

    return 0;
}

在这个例子中,定义了一个 lambda 表达式 f,它的参数使用了 decltype 推断出参数类型为 int&。由于在 lambda 表达式中使用的变量必须是可见的,因此在 lambda 表达式前面的捕获列表中使用了 [&],以让 lambda 表达式捕获 x。最后,调用 lambda 表达式 f,并将变量 x 作为参数传递给它,从而使 x 的值被加 1。

参考该博文

猜你喜欢

转载自blog.csdn.net/m0_51913750/article/details/131003652