decltype关键字总结
decltype概述
decltype也是用来进行类型推导的,这一点与auto比较类似。假如有这样一种场景,希望从表达式的类型推断出要定义的变量的类型,但是又不希望用表达式的值去初始化该变量(auto必须要初始化),那要怎么做?所以c++11新标准引入了decltype关键字,也叫说明符,它的作用主要就是用来返回操作数的数据类型的。
decltype推导分类
变量
#include <iostream>
#include <boost/type_index.hpp>
using namespace std;
using boost::typeindex::type_id_with_cvr;
int main()
{
const int i = 0;
const int& j = i;
//会保留const属性
decltype(i) i2 = 10;
cout << "i = " << type_id_with_cvr<decltype(i)>().pretty_name() << endl;
//会保留const,以及引用属性
decltype(j) j2 = i;
cout << "j = " << type_id_with_cvr<decltype(j)>().pretty_name() << endl;
//decltype也是可以用在类或者结构体当中的
decltype(A::a) p;
cout << "A::a = " << type_id_with_cvr<decltype(A::a)>().pretty_name() << endl;
cout << "p = " << type_id_with_cvr<decltype(p)>().pretty_name() << endl;
return 0;
}
表达式
#include <iostream>
#include <boost/type_index.hpp>
using namespace std;
using boost::typeindex::type_id_with_cvr;
int main()
{
/*来分析decltype后面的()圆括号当中是表达式的类型。
*/
int i = 10;
decltype(i + 10) j = 20;
cout << "i+10 = " << type_id_with_cvr<decltype(i+10)>().pretty_name() << endl;
int* p = &i;
cout << "*p = " << type_id_with_cvr<decltype(*p)>().pretty_name() << endl;
cout << "p = " << type_id_with_cvr<decltype(p)>().pretty_name() << endl;
return 0;
}
分析上面的结果,比较纳闷的是星号p为什么会是一个引用呢?按照正常理解应该是一个int啊,因为星号p在这里是一个表达式,而不会是一个变量,因为带有星号,在这里要专门的记一下,如果表达式的结果能够作为赋值语句等号左侧的值,比如*p = 10;那么decltype推导出来的就是一个引用。
这里还有一种情况要说明一下,如果()里面是一个变量,那么如果再加一个括号,比如decltype((变量)),,那么编译器就会把这个变量当中一个表达式去推导,而且因为变量是可以放在等号左侧的,所以返回的依旧是一个引用。
#include <iostream>
#include <boost/type_index.hpp>
using namespace std;
using boost::typeindex::type_id_with_cvr;
int main()
{
int i = 10;
cout << "(i) = " << type_id_with_cvr<decltype((i))>().pretty_name() << endl;
return 0;
}
函数
#include <iostream>
#include <boost/type_index.hpp>
#include <functional>
using namespace std;
using boost::typeindex::type_id_with_cvr;
int func()
{
std::cout << "func" << std::endl;
return 10;
}
int main()
{
/*从运行结果可以看到,实际上,func函数是没有被调用的*/
decltype(func()) i = 1;
cout << "func() = " << type_id_with_cvr<decltype(func())>().pretty_name() << endl;
cout << "i = " << type_id_with_cvr<decltype(i)>().pretty_name() << endl;
/*function是一个类模板*/
function<decltype(func)> p = func;
cout << "func = " << type_id_with_cvr<decltype(func)>().pretty_name() << endl;
p();//调用func()函数
return 0;
}
decltype的应用
decltype用在模板编程当中比较多,下面将decltype的应用分成了以下的几大类。
应付可变类型
在这里插入代码片
通过变量表达式抽取变量类型
#include <iostream>
#include <boost/type_index.hpp>
#include <vector>
using namespace std;
using boost::typeindex::type_id_with_cvr;
int main()
{
vector<int> vec;
vec.push_back(1);
vector<int>::size_type size = vec.size();
cout << size << endl;
//等价于vector<int>::size_type i
decltype(vec)::size_type i = vec.size();
cout << "vec= " << type_id_with_cvr<decltype(vec)>().pretty_name() << endl;
return 0;
}
结合auto构成返回类型后置语法
#include <iostream>
#include <boost/type_index.hpp>
using namespace std;
using boost::typeindex::type_id_with_cvr;
int tf(int i)
{
return i;
}
/*后置返回类型,利用tf函数的返回结果推导出tmp函数的返回类型*/
template<typename T>
auto tmp(T v) -> decltype(tf(v))
{
return tf(v);
}
int main()
{
tmp(10);
return 0;
}
如下的写法如果用decltype就不对了。
#include <iostream>
#include <boost/type_index.hpp>
using namespace std;
using boost::typeindex::type_id_with_cvr;
int tf(int i)
{
return i;
}
//错误,v是未定义的标识符,因为v出现在在tmp函数的形参里,比decltype出现的时机晚。
template <typename T>
decltype(tf(v)) tmp(T v)
{
return tf(v);
}
int main()
{
return 0;
}
总结:如果在现在的这种语义下,返回值后置的语法还是很必要的,当然,你用auto也行,这里只是演示以下错误的情形。
decltype(auto)用法
decltype(auto)的用法出现在c++14当中,建议将编译器的版本升级到c++14以上中,避免编译不通过的情况。
用于函数返回类型:
#include <iostream>
#include <boost/type_index.hpp>
using namespace std;
using boost::typeindex::type_id_with_cvr;
template<typename T>
T& fun(T& v)
{
return v;
}
int main()
{
int i = 10;
std::cout << fun(i) << std::endl;
fun(i) = 100;
std::cout << i << std::endl;
return 0;
}
现在一切正常,修改一下代码,
#include <iostream>
#include <boost/type_index.hpp>
using namespace std;
using boost::typeindex::type_id_with_cvr;
template<typename T>
auto fun(T& v)
{
return v;
}
int main()
{
int i = 10;
//fun(i) = 100; //错误,因为auto在这里会抛弃引用属性,所以fun(i)是一个右值,在这里赋值显然是不行的
cout << "func = " << type_id_with_cvr<decltype(fun(i))>().pretty_name() << endl;
return 0;
}
template<typename T>
decltype(auto) fun(T& v)
{
return v;
}
//当然用auto也可以解决这个问题
template <typename T>
auto& fun(T& v)
{
return v;
}
用在变量当中:
#include "ModbusTcpMaster.h"
#include <boost/type_index.hpp>
using namespace std;
using boost::typeindex::type_id_with_cvr;
int main(int argc, char** argv)
{
const int& x = 1;
auto y = x;
//如果用auto的话,const和&的属性都会丢弃
cout << "y = " << type_id_with_cvr<decltype(y)>().pretty_name() << endl;
//如果用decltype的话,const和&的属性都会保留
decltype(auto) z = x;
cout << "z = " << type_id_with_cvr<decltype(z)>().pretty_name() << endl;
return 0;
}
用在(),表达式当中:
#include "ModbusTcpMaster.h"
#include <boost/type_index.hpp>
using namespace std;
using boost::typeindex::type_id_with_cvr;
decltype(auto) fun()
{
int i;
return (i);
}
int main(int argc, char** argv)
{
fun() = 10; //int &,,相当于绑定了i,,,但是要注意虽然返回的是引用,但是要注意,i的作用域。
cout << "fun() = " << type_id_with_cvr<decltype(fun())>().pretty_name() << endl;
return 0;
}
decltype特点总结以及与auto对比
特点:
(1)decltype的自动类型推断也发生在编译期间,这点和auto一样
(2) decltype不会真正计算表达式的值
(3) const限定符,引用属性有可能会被auto抛弃,但是,decltype一般不会抛弃任何东西。
总结:decltype的用法很多,随着我们以后会阅读越来越多的代码,相信我们也会对它理解的更加的深刻。