标准库的典型内容
介绍C++标准库典型且常用的函数模板、类模板、别名模板等功能组件以及它们的实现细节
std::declval
基本概念和常规实例
std::decval–C++新标准中出现的函数模板,没有函数体(只有声明、没有实现),无法调用,一般用于与decltype,sizeof等关键字配合来进行类型推导、占用内存空间计算等
查看源码
add_rvalue_reference:是C++标准库中的类模板,他的能力是给进一个类型,它能够返回该类型的右值引用类型。
-
给进一个int类型,返回的是int&&
-
给进一个int&类型,返回的还是int&,这里用到引用折叠
-
给进一个int&&类型,返回的是int&&类型,依旧用到引用折叠
std::declval的功能:返回某个类型T的右值引用,不管该类型是否有默认构造函数或者该类型是否可以创建对象,返回某个类型T的右值引用,这个动作是在编译时完成的,所以很多人把std::declval也称为编译时工具
namespace nmsp1 {
class A {
public:
//构造函数
A(int i) {
printf("A::A()函数执行了,this=%p\n", this);
}
double myfunc() {
printf("A::myfunc()函数执行了,this=%p", this);
return 12.1;
}
};
}
using YT = decltype(std::declval<nmsp1::A>());//这里注意不要把std::declval<nmsp1::A>后面的圆括号丢掉,否则代码含义发生变化
//利用boost输出类型名比typeid(...).name()用法输出类型更准确
using boost::typeindex::type_id_with_cvr;
cout << "YT = " << type_id_with_cvr<YT>().pretty_name() << endl;//显示YT类型
//YT = class nmsp1::A &&
如果有需求需要得到myfunc的返回类型
- 传统做法:必须创建一个对象
nmsp1::A myaobj(1);
using boost::typeindex::type_id_with_cvr;
cout << "myaobj.myfunc的返回类型 = " << type_id_with_cvr<decltype(myaobj.myfunc())>().pretty_name() << endl;
//A::A()函数执行了,this=003CF8C7
// myaobj.myfunc的返回类型 = double
//如果myfunc函数权限为私有,此时不可以获取
- 不想创建对象获取到实际类型
using boost::typeindex::type_id_with_cvr;
cout << "A::myfunc的返回类型 = " << type_id_with_cvr<decltype(std::declval<nmsp1::A>().myfunc())>().pretty_name() << endl;
//A::myfunc的返回类型 = double
nmsp1::A&& ayinobj();//看起来是一个函数声明的语法,该函数返回的类型是A&&,可以看成返回一个A&&类型的对象,这种对象可以看成类A对象
//ayinobj();//看起来像调用ayinobj这个函数
//编译没错,链接不行
//ayinobj().myfunc();//同样纯编译没错
decltype(ayinobj().myfunc()) mydblvar;//定义一个double类型的变量mydblvar
//编译链接没错了
因为decltype推断类型时是不需要真的调用这个函数,也不需要调用A的myfunc,既然不需要调用,就没必要提供这种函数的函数体
sdt::declval
-
从类型转换的角度来说,将任意一个类型转换成右值引用类型
-
从假想创建出某类型对象的角度来说,配合decltype,令在decltype表达式中,不必经过该类型的构造函数就能使用该类型的成员函数
-
注意:std::declval不能调用,也不能创建任何对象,但std::declval能在不创建对象的情况下,达到创建了一个该类型对象的效果或者说程序员可以假定创建l一个该类型的对象
std::declval为什么返回右值引用类型
- 返回类型本身是不好的
namespace nmsp2 {
template<typename T>
T mydeclval()noexcept; //这里返回T
}
using boost::typeindex::type_id_with_cvr;
cout<<"mydeclval()的返回类型 = "<<type_id_with_cvr<decltype(nmsp2::mydeclval<nmsp1::A>())>().pretty_name() << endl;
cout << "mydeclval().myfunc的返回类型 = " << type_id_with_cvr<decltype(nmsp2::mydeclval<nmsp1::A>().myfunc())>().pretty_name() << endl;
//mydeclval()的返回类型 = class nmsp1::A
//mydeclval().myfunc的返回类型 = double
在原来类A的基础上增加私有的析构函数
namespace nmsp1 {
class A {
public:
//构造函数
A(int i) {
printf("A::A()函数执行了,this=%p\n", this);
}
double myfunc() {
printf("A::myfunc()函数执行了,this=%p", this);
return 12.1;
}
private:
~A()
{
}
};
}
cout<<"mydeclval()的返回类型 = "<<type_id_with_cvr<decltype(nmsp2::mydeclval<nmsp1::A>())>().pretty_name() << endl;
cout << "mydeclval().myfunc的返回类型 = " << type_id_with_cvr<decltype(nmsp2::mydeclval<nmsp1::A>().myfunc())>().pretty_name() << endl;//error:无法访问私有的析构函数
cout << "sizeof(mydeclval<A>()) = " << sizeof(nmsp2::mydeclval<nmsp1::A>()) << endl;//error
//因为返回类型本身导致为了遵循语义限制,编译器内部创建了临时的A类对象
//为了绕开予以限制,再设计mydeclval函数模板是,就不要返回T,可以返回T&,也可以返回T&&,
//这样从遵循予以限制方面来说,就不会创建临时的A类对象,这就是返回左值引用右值引用的好处
//上处修改成引用就没有报错
namespace nmsp2 {
template<typename T>
T& mydeclval()noexcept; //这里返回T
}
返回左值引用还是右值引用
class A {
public:
//构造函数
A(int i) {
printf("A::A()函数执行了,this=%p\n", this);
}
double myfunc() {
printf("A::myfunc()函数执行了,this=%p", this);
return 12.1;
}
private:
~A()
{
}
};
template<typename T>
T& mydeclval()noexcept;
using boost::typeindex::type_id_with_cvr;
cout << "decltype(mydeclval<A>())的返回类型 = " << type_id_with_cvr<decltype(mydeclval<A>())>().pretty_name() << endl;
cout << "decltype(mydeclval<A&>())的返回类型 = " << type_id_with_cvr<decltype(mydeclval<A&>())>().pretty_name() << endl;
cout << "decltype(mydeclval<A&&>())的返回类型 = " << type_id_with_cvr<decltype(mydeclval<A&&>())>().pretty_name() << endl;
//decltype(mydeclval<A>())的返回类型 = class A&
//decltype(mydeclval<A&>())的返回类型 = class A&
//decltype(mydeclval<A&&>())的返回类型 = class A&
template<typename T>
T&& mydeclval()noexcept;
using boost::typeindex::type_id_with_cvr;
cout << "decltype(mydeclval<A>())的返回类型 = " << type_id_with_cvr<decltype(mydeclval<A>())>().pretty_name() << endl;
cout << "decltype(mydeclval<A&>())的返回类型 = " << type_id_with_cvr<decltype(mydeclval<A&>())>().pretty_name() << endl;
cout << "decltype(mydeclval<A&&>())的返回类型 = " << type_id_with_cvr<decltype(mydeclval<A&&>())>().pretty_name() << endl;
//decltype(mydeclval<A>())的返回类型 = class A &&
//decltype(mydeclval<A&>())的返回类型 = class A &
//decltype(mydeclval<A&&>())的返回类型 = class A &&
看上面的结果不难想象为啥标注库使用的是返回右值,返回右值拿到的类型更全面
调用引用限定符修饰的成员函数范例
class ALR {
public:
void onArryValue() {
cout << "ALR::onArryValue()函数执行了" << endl;
}
void oLvalue()& //只能被类ALR的左值对象调用
{
cout << "ALR::oLvalue()函数执行l" << endl;
}
void oRvalue()&& //只能被类ALR的右值对象调用
{
cout << "ALR::oRvalue()函数执行l" << endl;
}
};
template<typename T>
//T& mydeclval()noexcept;
T&& mydeclval()noexcept;
ALR alr; //左值对象alr
alr.oLvalue();
//alr.oRvalue();//无法将左值绑定到右值引用
ALR().oRvalue();//这是临时对象,也是右值对象
decltype(mydeclval<ALR>().onArryValue());
decltype(mydeclval<ALR&>().oLvalue());
decltype(mydeclval<ALR&&>().oRvalue());
//如果
template<typename T>
T& mydeclval()noexcept;
decltype(mydeclval<ALR&&>().oRvalue());//Error
推导函数返回值范例
namespace nmsp2 {
int myfunc(int a, int b)//函数类型一般由函数的返回值和参数决定
{
return a + b;
}
template<typename T_F,typename... U_Args>
decltype(std::declval<T_F>()(std::declval<U_Args>()...)) TestFnRtnImpl(T_F func, U_Args... args)
{
//decltype(std::declval<T_F>()(std::declval<U_Args>()...))的作用:根据函数类型以及函数参数类型推断函数返回值类型
auto rtnvalue = func(args...);
return rtnvalue;
}
}
auto result = nmsp2::TestFnRtnImpl(nmsp2::myfunc, 5, 8);//T_F = int (*)(int,int)
cout << result << endl;
int (*fp_var)(int x, int y);//函数指针类型fp_var = int(*)(int,int)
int(*&&refer_fp_var)(int x, int y) = std::move(fp_var);//函数指针的右值引用,int (*&&)(int,int)
fp_var = nmsp2::myfunc;
cout << fp_var(1, 2) << endl;
cout << refer_fp_var(1, 2) << endl;
T_F = int (*)(int,int)类型,也就是函数指针类型
decltype(std::declval<T_F>()(std::declval<U_Args>()…)) = int ,也就是myfunc的返回类型
decltype(std::declval<T_F>()) = 是int (*&&)(int,int)函数指针的右值引用类型,decltype(std::declval<U_Args>()…)这种写法:推导出来的是两个int&&
namespace nmsp3 {
//返回类型后置语法
template<typename T_F, typename... U_Args>
auto TestFnRtnImpl(T_F func, U_Args... args)->decltype(func(args...))
{ //->decltype(func) int(*)(int,int)
auto rtnvalue = func(args...);
return rtnvalue;
}
}