相关系列文章
目录
2.1.在实际使用用时,std::declval一般结合decltype使用
2.3.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,因为这个条件判断是在编译的时候发生的。
3.参考
<<深入应用C++11代码优化与工程级应用>>