网站首页 全球最实用的IT互联网站!

人工智能P2P分享Wind搜索发布信息网站地图标签大全

当前位置:诺佳网 > 软件工程 > 后端开发 > C++ >

自动类型推导

时间:2026-03-10 13:53

人气:

作者:admin

标签:

导读:c11中添加了自动推导变量的类型auto,以及decltype表示函数的返回值。 auto auto可以像别的语言一样自动推导出变量的实际类型。 在实际中,auto像是一个”占位符“,使用auto声明的变量必...

c++11中添加了自动推导变量的类型auto,以及decltype表示函数的返回值。

auto

auto可以像别的语言一样自动推导出变量的实际类型。

在实际中,auto像是一个”占位符“,使用auto声明的变量必须要进行初始化,以让编译器推导出它的实际类型,在编译时将auto换成真正的类型。

语法:

auto 变量名 = 变量值

实际使用例子:

#include <iostream>
using namespace std;

int main() {
	//没有const修饰
	auto x = 3.14; //double
	auto y = 520;  //int
	auto z = 'a';  //char
	//auto nb;       //语法错误 
	//auto double nbl; //语法错误

	int temp = 110;
	auto* a = &temp; //&temp:int*   auto*:int*     auto:int
	auto b = &temp;  //auto:int*
	auto& c = temp;  //auto:int
	auto d = temp;   //auto:int

	//有const修饰
	int tmp = 250;
	const auto a1 = tmp;  //auto:int   a1:const int
	auto a2 = a1;         //a1不是指针也不是引用       auto:int        a2:int
	const auto& a3 = tmp; //a3:const int&      auto:int
	auto& a4 = a3;        //a3是引用类型       a3:const int&     a4:const int&    auto&:const int&   auto:const int

	auto* pt4 = &a1;      //&a1是地址     a1:const int   pt4:const int*    auto:const int

	system("pause");
	return 0;
}

需要注意的是:

在auto和指针、引用结合在一起时,推导的结果会保留const、volatile关键字(volatile表示变量,经常修改的变量)

  • 当变量不是指针或者引用类型时,推导的结果中不会保留const、volatile关键字。
  • 当变量时指针或者引用类型时,推导的结果中会保留const、volatile关键字。

就如上述代码中的 //有const修饰 后面的代码,需要注意变量是否为指针或者引用类型。

auto不能推导的4个情况

1. 不能作为函数参数使用,因为只有在函数调用的时候才会给函数参数传递实参,auto要求必须要给修饰的变量赋值,因此二者不矛盾。
int func(auto a, auto b) {  //error
	cout << "a: " << a << ", b: " << b << endl;
}
2.不能用于类的非静态成员变量的初始化
class Test {
	auto v1 = 0;                  //error
	static auto v2 = 0;           //error,类的静态非常量成员不允许在类内部直接初始化
	static const auto v3 = 10;    //ok
};
3. 不能使用auto关键字定义数组
int func() {
	int array[] = { 1, 2, 3, 4, 5 }; //定义数组
	auto t1 = array;                 //ok, t1被推导为 int* 类型
	auto t2[] = array;               //error, auto无法定义数组
	auto t3[] = { 1, 2, 3, 4, 5 };   //error, auto无法定义数组
}
4. 无法使用auto推导出模板参数
template <typename T>
struct Test {};

int func() {
	Test<double> t;
	Test<auto> t1 = t;     //error,无法推导出模板类型
	return 0;
}

auto常用用途

1. 用于STL容器的遍历

在遍历时我们会写:map<int, string>::iterator it = mp.begin();这样的迭代器,在有了auto之后可以用auto代替map<int, string>::iterator

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

int main() {
	//key:int , value:string
	map<int, string>mp;
	mp.insert(make_pair(1, "ace"));
	mp.insert(make_pair(2, "sabo"));
	mp.insert(make_pair(3, "luffy"));
	//map<int, string>::iterator it = mp.begin();
	auto it = mp.begin();
	for (; it != mp.end(); it++) {
		cout << "key: " << it->first << ", value: " << it->second << endl;
	}
	system("pause");
	return 0;
}

如上述代码,在有了auto之后迭代器的定义简单方便了很多。

2. 用在泛型编程

在使用模板的时候,很多情况下我们不知道变量应该定义成什么类型,比如下面的代码:

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

class T1 {
public:
	static int get() {
		return 10;
	}
};
class T2 {
public:
	static string get() {
		return "hello, world";
	}
};

//template<class T, typename P>
template <class T>
void func() {
	auto ret = T::get();
	//P ret = T::get();
	cout << "ret: " << ret << endl;
}

int main() {
    func<T1>();
    func<T2>();
	//func<T1, int>();
	//func<T2, string>();
    system("pause");
    return 0;
}

上述代码中T1::get()返回int类型,T2::get()返回string类型。在模板中调用时就能不确定返回什么类型的值,使用auto解决了这一问题,如果不用auto只能像注释中的那样多定义一个模板参数来确定返回的值时什么类型。

decltype

C++11增加了decltype关键字,它是在编译器编译的时候推导出一个表达式的类型;

decltype不需要定义变量,不需要初始化变量也可以推导类型。语法:decltype(表达式)

decltype是"declare type"的缩写,译为"声明类型"。decltype的推导是在编译时完成的,只是用于推导表达式的类型,并不会计算表达式的值,如下:

int a = 10; 
decltype(a) b = 99;            //a:int            b:int
decltype(a+3.14) c = 3.14159;  //a+3.14:double    c:double
decltype(a+b*c) d = 234.2343;  //a+b*c:double     d:double
decltype(a) e;                 //a:int            e:int 

decltype只是使用了括号中表达式的类型,后面的变量定义和括号中表达式值的大小无关。

auto只能推导已初始化的变量类型,decltype推导的可以不进行初始化。

decltype的推导规则

decltype的3个场景的使用规则:

1. 表达式为普通变量或者普通表达式或者类表达式

表达式为普通变量或者普通表达式后者类表达式时,decltype推导出的类型和表达式的类型是一样的。

#include <iostream>
using namespace std;

class Test {
public:
	int num = 9;
	string text;
	static const int value = 110;
};
int main() {
	int x = 99;
	const int& y = x;
	decltype(x) a = x;                 //x:int                           a:int    
	decltype(y) b = x;                 //y:const int&                    b:const int&
	decltype(Test::value) c = 0;       //Test::value : const int         c:const int
	Test t;                        
	decltype(t.text) d = "hello, world"; //t.text : string               d:string
    
	system("pause");
	return 0;
}
2.表达式是函数调用

表达式是函数调用的时候,decltype推导出的类型和函数返回值是一致的。

#include <iostream>
using namespace std;

class Test {
public:
	int num = 9;
	string text;
	static const int value = 110;
};
//函数声明
int func_int() {};
int& func_int_r() {};
int&& func_int_rr() {};
const int func_cint() {};
const int& func_cint_r() {};
const int&& func_cint_rr() {};
const Test func_ctest() {};

int main() {
	int n = 100;
	decltype(func_int()) a = 0;    //func_int():int            a:int
	decltype(func_int_r()) b = n;  //func_int_r():int&         b:int&
	decltype(func_int_rr()) c = 0; //func_int_rr():int&&       c:int&&
	decltype(func_cint()) d = 0;   //func_cint():const int     d:int    (这里是因为func_cint()函数返回的值是一个纯右值,就会被推导为int;对于右值而言只有类类型可以携带const、volatile限定符,其他需要忽略这两个限定符)
	decltype(func_cint_r()) e = 0; //func_cint_r():const int&      e:const int&
	decltype(func_cint_rr()) f = 0; //func_cint_rr():const int&&     f:const int&&
	decltype(func_ctest()) g = Test(); //func_ctest:const Test       g:const Test

	system("pause");
	return 0;
}

上述代码中func_cint()返回的是一个纯右值(再func_cint()运行之后不再存在数据,也就是返回的是临时性的数据),对于纯右值而言,只有类类型会携带const、volatile限定符,其他需要忽略这两个限定符。

上例中用到了int&&等右值引用,这里补充一下右值引用相关内容(以int&&为例):

int&是左值引用,绑定到可命名、可持久存在的对象;

C++11引入了int&&右值引用

int&&为右值引用,绑定到临时对象或将要被销毁的对象,用于移动语义和完美转发。

int a = 10;
int& b = a;   //正确
int& c = 30;  //错误
int&& r = a;  //错误
int&& d = 30; //正确
int&& e = 20+10; //正确

int&&的最重要的应用:移动语义

在移动右值的时候可以避免深拷贝,性能更高:

vector<int> a = {1,2,3};
vector<int> b = std::move(a);

其中std::move()是c++11引入的一个函数,它的作用是:把一个对象强制转换成右值引用,从而触发移动语义,注意:std::move()本身并不会移动任何数据,它只是一个类型转换。

#include <iostream>
#include <vector>
using namespace std;

int main() {
 vector<int> a = {1,2,3};

 vector<int> b = std::move(a);

 cout << a.size() << endl; // 可能为0
}

上述代码中的vector<int> b = std::move(a)发生的是:

a->内存数据

移动后

b->内存数据

a->空

移动之后b接管了a的资源,a变成空对象

移动有别于拷贝,新的变量接管了原来变量的内存数据,原来变量就成为空对象了。

注意:

std::move()不会移动对象。只是说:这个对象的资源可以被拿走了。

真正移动的是:移动构造函数 或者 移动赋值运算符

3.表达式是一个左值,或者被括号()包围

表达式是一个左值,或者被括号()包围时,使用decltype推导出的是表达式类型的引用(如果有const、volatile限定符不能忽略)

#include <iostream>
using namespace std;

class Test {
public:
	int num = 9;
	string text;
	static const int value = 110;
};
int main() {
	const Test obj;
	//带有括号的表达式
	decltype(obj.num) a = 0;    //obj.num:int     a:int
	decltype((obj.num)) b = a;  //obj.num:int     b:const int&       加了括号是引用,这里就是一个obj.num的引用,obj是const的,obj的里面的成员就也是const的
	//加法表达式
	int n = 0, m = 0;
	decltype(n + m) c = 0;         //n+m:int        c:int         这里就是一个普通的表达式
	decltype(n = n + m) d = n;     //n=n+m :int     d:int&        这里n是一个左值,所以是引用

	system("pause");
	return 0;
}

如上述代码obj.numint类型,(obj.num)int&类型,所以b是const int&类型

n + m是普通的表达式,是右值,n = n + m这里的n进行赋值后是左值,所以是int&类型

decltype的常用用途

decltype多引用在泛型编程中,比如我们编写一个类模板,在里边添加遍历容器的函数,代码如下:

#include <iostream>
#include <list>
using namespace std;

template<class T>
class Container {
public:
	void print(T& t) {
		for (m_it = t.begin(); m_it != t.end(); m_it++) {
			cout << "value: " << *m_it << endl;
		}
		cout << endl;
	}
private:
	decltype(T().begin()) m_it;            //需要补充内容:T()是T的临时变量。。。。。。
};
int main() {
	list<int> ls{ 1, 2, 3, 4, 5, 6, 7 };
	Container<list<int>> c;
	c.print(ls);

	const list<int> ls1{ 1, 2, 3, 4, 5, 6, 7 };
	Container<const list<int>> c1;
	c1.print(ls);

	system("pause");
	return 0;
}

如果我们15行写T::iterator m_it;编译器会报错,无法得知这里要定义一个什么类型的iterator。

有了decltype就很好的解决了这个问题:decltype(T().begin()) m_it这里推导出的一定是一个迭代器类型的。

其中T()是T的临时变量,它在这里创建一个T类型的临时对象(默认构造),以便于可以调用begin()函数,从而让decltype可以推导出迭代器类型。

返回类型后置

在泛型编程中,可能需要通过参数的运算来确定返回值的类型,比如:

#include <iostream>
using namespace std;

template<typename R, typename T, typename U>
R add(T t, U u) {
	return t + u;
}
int main() {
	int x = 520;
	double y = 13.14;
	auto res = add<decltype(x+y),int,double>(x, y);
	cout << "res = " << res << endl;

	system("pause");
	return 0;
}

上述代码中R是由传入的阐述 T 和 U 决定的,在调用模板函数时,我们通多decltype(x+y)自动推导函数返回值的类型。

但是在实际开发中,很少能知道add()函数的内部是怎么样的,也就无法确定返回值的类型了。

在c++11中增加了返回值类型后置语法,它是将decltype和auto结合起来完成返回类型的推导。语法如下:

//符号 -> 后边跟随的是函数返回值的类型
auto func(参数1, 参数2, ...) -> decltype(参数表达式)

其中decltype(参数表达式)中的表达式不需要一定和func()内部的操作有关,只做类型推导的作用。auto会追踪decltype()推导出的类型。

使用返回值类型后置语法后,之前的案例可以改为:

#include <iostream>
using namespace std;

template<typename T, typename U>
auto add(T t, U u) -> decltype(t + u){
	return t + u;
}

int main() {
	int x = 520;
	double y = 13.14;
	auto res = add(x, y);
	cout << "res = " << res << endl;

	system("pause");
	return 0;
}

使用了返回值类型后置后,函数的调用变的更加方便;再加上方法的默认模板参数,这里的add就可以不需要<>直接传输参数add(x,y)

温馨提示:以上内容整理于网络,仅供参考,如果对您有帮助,留下您的阅读感言吧!
相关阅读
  • 指针空置类型-nullptr

    指针空置类型-nullptr

    先看一段代码: #include lt;iostreamgt; using namespace std; void func(char* p) { cout lt;lt; q...
  • 基于范围的for循环

    基于范围的for循环

    c11基于范围的for循环,语法: for (Type declaration : expression) { // 循环体 } 在上面的...
本类排行
相关标签
本类推荐

CPU | 内存 | 硬盘 | 显卡 | 显示器 | 主板 | 电源 | 键鼠 | 网站地图

Copyright © 2025-2035 诺佳网 版权所有 备案号:赣ICP备2025066733号
本站资料均来源互联网收集整理,作品版权归作者所有,如果侵犯了您的版权,请跟我们联系。

关注微信