C++之std::declval的具体使用

 更新时间:2026年07月01日 09:09:41   作者:流星雨爱编程  
本文介绍了C++11引入的模板函数std::declval,它能将任意类型转换成右值引用类型,文中通过示例代码介绍的非常详细,对大家的学习或者工作具有一定的参考学习价值,需要的朋友们下面随着小编来一起学习学习吧

1.简介

std::declval是C++11引入的一个模板函数,将任意类型 T 转换成右值引用&&类型,在 decltype 表达式中不必经过构造函数就能使用成员函数;通常在模板中使用 std::declval时,模板接受的模板实参通常可能无构造函数,但有同一成员函数,均返回所需类型。

在VS2019中函数的原型:

template <class _Ty>
add_rvalue_reference_t<_Ty> declval() noexcept;

通过 add_rvalue_reference_t 返回模板类型_Ty的右值引用_Ty&&,   add_rvalue_reference_t的定义如下:

add_rvalue_reference_t是C++标准中模板类,它是将传入的模板类_Ty转换为_Ty&&, 即返回它的又值引用类型,如:

1) 如果_Ty是int, 根据引用折叠规则和上面A定义返回的就是int&&类型

2) 如果_Ty是int&,根据引用折叠规则上面B定义返回的就是int&&类型

3) 如果_Ty是int&&,根据引用折叠规则和上面A定义返回的就是int&&类型

2.用法

2.1.在实际使用用时,std::declval一般结合decltype使用

//调用引用限定符修饰的成员函数
class ALR
{
public:
	void onAnyValue()
	{
		qDebug() << "ALR::onAnyValue()函数执行了!" << endl;
	}

	void onLvalue()& //只能被类ALR的左值对象调用
	{
		qDebug() << "ALR::onLvalue()函数执行了!" << endl;
	}

	void onRvalue()&& //只能被类ALR的右值对象调用
	{
		qDebug() << "ALR::onRvalue()函数执行了!" << endl;
	}
};

template <typename T>
T&& mydeclval() noexcept;

int main() {
	ALR alr;  //左值对象alr
	alr.onLvalue();
	//alr.onRvalue(); //编译错误,因为onRvalue只能被类A的右值对象调用
	ALR().onRvalue();  //临时对象是右值对象
	//ALR().onLvalue();  //编译错误,因为onLvalue只能被类A的左值对象调用

	decltype(mydeclval<ALR>().onAnyValue());
	decltype(mydeclval<ALR&>().onLvalue()); //返回的 类型是class ALR &,代表返回的是左值对象,左值对象调用onLvalue没问题
	decltype(mydeclval<ALR&&>().onRvalue()); //返回的 类型是class ALR &&,代表返回的是右值对象,右值对象调用onRvalue没问题
	//decltype(mydeclval<ALR&>().onRvalue());//返回的 类型是class ALR &,代表返回的是左值对象,左值对象调用onRvalue是错误的
	//decltype(mydeclval<ALR&&>().onLvalue()); //返回的 类型是class ALR &&,代表返回的是右值对象,右值对象调用onLvalue是错误的
    return 0;
}

decltype(mydeclval<ALR>().onAnyValue()) 、decltype(mydeclval<ALR&>().onLvalue())和decltype(mydeclval<ALR&&>().onRvalue()) 不会调用onAnyValue函数,也不会调用onLvalue函数和onRvalue函数,所以不会报错。

同理,可理解decltype(std::declval<ALR>().onAnyValue())含义,同样它也不会调用ALR的onAnyValue函数。

2.2.在函数模板和类模板中推导参数的类型和返回值的类型

如:

template <typename T>
class CMessageEntityManagerTemplate
{
    //方法1
    //using KEY = decltype(((T*)0)->id());
    //方法3
    using KEY = decltype(std::declval<T>().id());
    ...
    ...
}

模板类CMessageEntityManagerTemplate中的数据类型KEY通过T.id()函数在编译时期推导出的,而不会去调用id()函数的。

再比如:

#include <utility>
#include <iostream>
 
struct Default { int foo() const { return 1; } };
 
struct NonDefault
{
    NonDefault() = delete;
    int foo() const { return 1; }
};
 
int main()
{
    decltype(Default().foo()) n1 = 1;                   // n1 的类型是 int
//  decltype(NonDefault().foo()) n2 = n1;               // 错误:无默认构造函数
    decltype(std::declval<NonDefault>().foo()) n2 = n1; // n2 的类型是 int
    std::cout << "n1 = " << n1 << '\n'
              << "n2 = " << n2 << '\n';
    return 0;
}

输出:

n1 = 1
n2 = 1

从这个例子可以看出,std::declval不管传入对象 NonDefault 是否可以构造,都不会报错,那是因为std::declval返回的是NonDefault&&,从而避免了返回 NonDefault 时编译器内部不能创建临时对象导致编译报错的发生。

2.3.std::declval还可以用来判断某个类是否具备某个函数

//判断类是否具有某个静态函数
#define HAS_MEMBER_EX(member)\
template<typename T, typename... Args> struct has_member_ex_##member{\
private:\
    template<typename U> \
	static auto Check(int) -> decltype(U::member(std::declval<Args>()...), std::true_type()); \
	template<typename U> \
	static std::false_type Check(...); \
public:\
	enum { value = std::is_same<decltype(Check<T>(0)), std::true_type>::value }; \
}; \

HAS_MEMBER_EX(getDataSize)

//判断类是否具有某个非静态成员函数
#define HAS_NON_STATIC_MEMBER_EX(member)\
template<typename T, typename... Args> struct has_non_static_member_ex_##member{\
private:\
    template<typename U> \
	static auto Check(int) -> decltype(std::declval<T>().member(std::declval<Args>()...), std::true_type()); \
	template<typename U> \
	static std::false_type Check(...); \
public:\
	enum { value = std::is_same<decltype(Check<T>(0)), std::true_type>::value }; \
}; \

HAS_NON_STATIC_MEMBER_EX(getDataSize)

调用方法:

using PUInt32 = unsigned int;
using PUInt16 = unsigned short;

//参数配置
struct stParamConfig
{
	PUInt32  rate;
	PUInt32  speed;
	//...
public:
	static PUInt16 getDataSize() {
		return 10;
	}
};

//业务数据
struct stAppData
{
	char           data[1024];
	PUInt32        size;
	//...
public:
	PUInt32 getDataSize() const {
		return size;
	}
};

//数据包的结构体
struct stShortWavePacket
{
	//...
	void* pAppData;
	//...
};

/*组包功能:获取stType长度, 适应不同的结构体的取长度函数
  stType类型有:1)  stParamConfig
			   2)  stAppData
			   3)  int, double等简单数据结构类型
*/
template <PUInt32 type, typename stType>
int  encodeCommonCmdData(char* data, int len, const stShortWavePacket* pPacket)
{
	//...
	const stType* pAppType = static_cast<const stType*>(pPacket->pAppData);
	assert(pAppType);
	//...

	PUInt32 len = 0;
	if constexpr (has_member_ex_getDataSize<stType>::value) {
		len = stType::getDataSize();
	}
	else if constexpr (has_non_static_member_ex_getDataSize<stType>::value) {
		len = pAppType->getDataSize();
	}
	else {
		len = sizeof(stType);
	}

	//...

	return 1;
}

这里条件判断必须加上constexpr,因为这个条件判断是在编译的时候发生的。

到此这篇关于C++之std::declval的具体使用的文章就介绍到这了,更多相关C++ std::declval内容请搜索脚本之家以前的文章或继续浏览下面的相关文章希望大家以后多多支持脚本之家!

您可能感兴趣的文章:

相关文章

  • C/C++实现发送与接收HTTP/S请求的示例代码

    C/C++实现发送与接收HTTP/S请求的示例代码

    HTTP(Hypertext Transfer Protocol)是一种用于传输超文本的协议,它是一种无状态的、应用层的协议,用于在计算机之间传输超文本文档,通常在 Web 浏览器和 Web 服务器之间进行数据通信,本文给大家介绍了C/C++发送与接收HTTP/S请求,需要的朋友可以参考下
    2023-11-11
  • C++ 函数的介绍

    C++ 函数的介绍

    本篇主要介绍了函数的基础概念以及一些特殊的函数方法和类型,函数重载以及函数指针,下面一起进入文章学习详细的内容吧,需要的朋友也可以参考一下
    2021-12-12
  • C语言中bool和float的用法实例解析

    C语言中bool和float的用法实例解析

    这篇文章主要介绍了C语言中bool类型和float类型的相关资料,bool类型用于声明布尔变量,只有true和false两种值,float类型用于存储单精度浮点数,文中通过代码介绍的非常详细,需要的朋友可以参考下
    2024-11-11
  • C++ 头文件系列(set)详解

    C++ 头文件系列(set)详解

    一般而言,每个C++/C程序通常由头文件和定义文件组成。头文件作为一种包含功能函数、数据接口声明的载体文件,主要用于保存程序的声明,而定义文件用于保存程序的实现 。
    2017-02-02
  • C++求最大公约数四种方法解析

    C++求最大公约数四种方法解析

    这篇文章主要为大家详细介绍了C++求最大公约数四种方法,文中示例代码介绍的非常详细,具有一定的参考价值,感兴趣的小伙伴们可以参考一下
    2020-09-09
  • C++实现LeetCode(147.链表插入排序)

    C++实现LeetCode(147.链表插入排序)

    这篇文章主要介绍了C++实现LeetCode(147.链表插入排序),本篇文章通过简要的案例,讲解了该项技术的了解与使用,以下就是详细内容,需要的朋友可以参考下
    2021-07-07
  • VS2022+libtorch+Cuda11.3安装测试教程详解(调用cuda)

    VS2022+libtorch+Cuda11.3安装测试教程详解(调用cuda)

    这篇文章主要介绍了VS2022+libtorch+Cuda11.3安装测试(调用cuda),本文通过图文并茂的形式给大家介绍的非常详细,对大家的学习或工作具有一定的参考借鉴价值,需要的朋友可以参考下
    2022-05-05
  • C/C++线程退出的四种方法小结

    C/C++线程退出的四种方法小结

    本文主要介绍了C/C++线程退出的四种方法小结,文中通过示例代码介绍的非常详细,对大家的学习或者工作具有一定的参考学习价值,需要的朋友们下面随着小编来一起学习学习吧
    2023-07-07
  • 一文详解C语言char类型中的存储

    一文详解C语言char类型中的存储

    C语言中的char是用于声明单个字符的关键字,这篇文章主要给大家介绍了关于C语言char类型中存储的相关资料,文中通过实例代码介绍的非常详细,需要的朋友可以参考下
    2023-01-01
  • C++模拟如何实现vector

    C++模拟如何实现vector

    这篇文章主要介绍了C++模拟如何实现vector问题,具有很好的参考价值,希望对大家有所帮助。如有错误或未考虑完全的地方,望不吝赐教
    2023-02-02

最新评论