C++之std::declval

简介

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

在VS2019中函数的原型:

template 
add_rvalue_reference_t<_Ty> declval() noexcept;

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

C++之std::declval_第1张图片add_rvalue_reference_t是C++标准中模板类,它是将传入的模板类_Ty转换为_Ty&&, 即返回它的又值引用类型,如:

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

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

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

用法

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 
T&& mydeclval() noexcept;

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

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

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

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

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

如:

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

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

再比如:

#include 
#include 
 
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().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 时编译器内部不能创建临时对象导致编译报错的发生。

3、std::declval还可以用来判断某个类是否具备某个成员函数

#define HAS_MEMBER_EX(member)\
template struct has_member_ex_##member{\
private:\
    template \
	static auto Check(int) -> decltype(U::member(std::declval()...), std::true_type()); \
	template \
	static std::false_type Check(...); \
public:\
	enum { value = std::is_same(0)), std::true_type>::value }; \
}; \

HAS_MEMBER_EX(getDataSize)

调用方法:

    template 
	int  parseCommonCmdData(const char* data, int len, stShortWavePacket* pPacket)
	{
        ...
		if constexpr (has_member_ex_getDataSize::value) {
			if (length != stType::getDataSize())
				return -1;
		}
        ...
    }

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

参考:

<<深入应用C++11代码优化与工程级应用>>

你可能感兴趣的:(#C++,c++,开发语言)