c++-----declval

std::declval

基本概念和常规实例

std::decval–C++新标准中出现的函数模板,没有函数体(只有声明、没有实现),无法调用,一般用于与decltype,sizeof等关键字配合来进行类型推导、占用内存空间计算等

查看源码:

 add_rvalue_reference:是C++标准库中的类模板,他的能力是给进一个类型,它能够返回该类型的右值引用类型。

  1. 给进一个int类型,返回的是int&&

  2. 给进一个int&类型,返回的还是int&,这里用到引用折叠

  3. 给进一个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

1.从类型转换的角度来说,将任意一个类型转换成右值引用类型

2.从假想创建出某类型对象的角度来说,配合decltype,令在decltype表达式中,不必经过该类型的构造函数就能使用该类型的成员函数

3.注意:std::declval不能调用,也不能创建任何对象,但std::declval能在不创建对象的情况下,达到创建了一个该类型对象的效果或者说程序员可以假定创建一个该类型的对象

 

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;
    }
}

 

https://blog.csdn.net/m0_51271123/article/details/121780256

posted @ 2024-05-13 14:39  白伟碧一些小心得  阅读(303)  评论(0编辑  收藏  举报