[笔记]c++: declval

本文介绍了C++中的std::declval,它是一个模板函数,用于在非求值上下文中获取类型T的临时对象的返回类型。在遇到无法实例化的类模板参数或函数对象时,std::declval可以帮助我们规避构造限制。文章通过示例展示了std::declval的使用场景,并探讨了其实现细节,包括如何确保其只在非求值上下文中使用。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

来源c++20高级编程

std::declval

declval不是语言特性, 非关键字, 而是模板函数


考虑场景: 需要函数对其参数进行调用得到的返回类型

假如有下类

struct F
{
    double operator()(char,int);
    float operator()(int);
};

 并实现

template<typename F,typename ...Args>
using InvokeResultFunc= ? ;//如何实现

总之我们希望得到以下结果

using T1=InvokeResultFunc<F,char,int>; //T1=double
using T2=InvokeResultFunc<F,char>;      //T2=float

 


一种初步实现

template<typename F,typename ...Args>
using InvokeResultFunc= decltype(F{}(Args{}...)) ;

F进行实例化得到函数对象, 再对每个参数进行实例化,

最后通过decltype操作获取调用后的返回类型

需要注意的是实例化并不是在真正的内存上构造出对象, 仅在编译期非求值于上下文中, 仅用于构造合法的语句

decltype sizeof表达式里属于非求值上下文, 例

int x=1;
int y=sizeof(x++);
decltype(x++) z=1;

cout<<(  x  )<<endl; //x=1, 

最后x=1

sizeof和decltype中的x仅用于求其大小或类型, 不会对x++进行真正运算

然而

类模板参数 函数对象不可构造时, 其中或没有默认构造函数, 或构造函数是私有的, 总之就是无法构造F{}(Args{}...)时, 上述实现不可用(即虽然在非求值上下文里不会真正构造, 但仍需符合语法规则)

以下代码将会报错( 参数和函数对象的构造函数私有, 无法构造)

template<typename F,typename ...Args>
using InvokeResultFunc= decltype(F{}(Args{}...)) ;//如何实现

class A
{
private: //参数不可构造
    A(){}
};

struct F
{
private: //函数对象不可构造
    double operator()(A,int);
    float operator()(int);
};

using T1=InvokeResultFunc<F,char,int>; 

这时可以用declval构造, 将不受以上约束

template<typename F,typename ...Args>
using InvokeResultFunc= decltype(declval<F>()(declval<Args>()...));

第一个declval构造函数对象F,  第二个declval构造参数对象


实际上declval是返回一个转发引用的对象(T&&), 主要因为引用类型可以是非完备类型, 即只需声明, 无需定义, 如

class A;   //非完备类型
class B{}; //完备类型

declval只能用于decltype, sizeof等非求值上下文中

以下实现确保程序员只能在非求值上下文中使用declval

首先定义declval_(加个下划线避免与std::declval重名)

template<typename T>
T && declval_();

但实际上只有声明, 没有定义, 这正好符合要求:

由于在非求值上下文中使用该函数不会真正调用该函数(只需取得其返回类型), 而在其他地方使用会出现declval模板函数未定义的链接错误(由于没有实现) 

template<typename T>
T && declval_();
int main()
{
    //链接错误: undefined reference to `int&& declval_<int>()'
    //declval_<int>();

    //可以使用
    decltype(declval_<int>());
}

这个错误信息不够友好, 将其修改添加错误信息

struct declval_protector
{
    static constexpr bool value=false;
};
template<typename T>
T && declval_()
{
    static_assert(declval_protector<T>::value,
            "declval只能在sizeof/declval等非求值上下文中使用"  );
}

没有直接将static_assert的条件设置为false, 当在非求值上下文里使用时将会忽略跳过该断言

其他场景使用则会出现错误

(这个实现对于void而言仍有问题, 由于void没有引用类型void&&)

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值