c++std::declval函数模板详解

std::declval详解

std::declval认识

概念

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

常规范例

根据std::declval的源码写的例程,先熟悉一下。

#include 
#include 
using namespace std;
template<class ...T>
using m_void_t = void;

/*泛化*/
template<class T, class U = void>
struct m_Add_reference {
	m_Add_reference()
	{
		std::cout << " a1 " << std::endl;
	}
	using m_Lvalue = T;
	using m_Rvalue = T;
};

/*偏特化*/
template<class T>
struct m_Add_reference<T, m_void_t<T&> > {
	m_Add_reference()
	{
		std::cout << " a2 " << std::endl;
	}
	using m_Lvalue = T&;
	using m_Rvalue = T&&;
};

int main()
{
	/*没有void&,所以是生成了泛化版本,如果有合适的会调用的是偏特化版本*/
	m_Add_reference<void> a; // m_void_t,,,error
	m_Add_reference<int> a1; 
    return 0;
}

下面我们在来看标准库当中std::declval的一些具体用途

#include 
#include 
#include 
using namespace std;
class A
{
public:
	A(int i)
	{
		std::cout << "A(i)" << std::endl;
	}

	int fun()
	{
		return 1;
	}
};
using YT = decltype(std::declval<A>());
using Af = decltype(std::declval<A>().fun());
int main()
{
	using boost::typeindex::type_id_with_cvr;

	cout << type_id_with_cvr<YT>().pretty_name() << endl;

	cout << type_id_with_cvr<Af>().pretty_name() << endl;

	cout << "A =" << sizeof(std::declval<A>()) << endl;

	cout << "A =" << sizeof(A) << endl;
    return 0;
}

在上面的例子当中,我们利用std::declval可以在不创建类A对象的情况下,就可以知道类A对象里面fun函数的返回值类型。
c++std::declval函数模板详解_第1张图片
注意,并没有调用类A的构造函数。

std::declval为什么返回右值引用类型

我们来分析下标准库当中的std::declval为什么返回右值引用

返回类型本身是不好的

#include 
#include 
#include 
using namespace std;
class A
{
public:
	A(int i)
	{
		std::cout << "A(i)" << std::endl;
	}

	int fun()
	{
		return 1;
	}
private:
	~A()
	{
	}
};

template<class T>
T m_delcavl() noexcept;
int main()
{
	using boost::typeindex::type_id_with_cvr;
	cout << type_id_with_cvr<decltype(m_delcavl<A>())>().pretty_name() << endl;
	cout << type_id_with_cvr<decltype(m_delcavl<A>().fun() )>().pretty_name() << endl;
    return 0;
}

c++std::declval函数模板详解_第2张图片
在上面的例程当中,增加了一个私有的构造函数,
并且增加了一个返回类型本身的函数模板,但是编译时报错了,原因就是这里会生成一个临时对象,导致无法调用析构函数报错了,,如果把那个函数模板改为左值引用或者右值引用,你会发现编译时就不会报错了,这里就是在这种情况下说明了返回类型本身是不好的。那返回左值引用和右值引用都可以,为什么标准库的declval为什么写的是返回右值引用呢?
我们接着讲。

返回右值引用还是左值引用

这一小节通过一些小的范例演示,给出为什么标准库当中的std::declval返回的是右值引用。

#include 
#include 
#include 
using namespace std;
class A
{
public:
	A(int i)
	{
		std::cout << "A(i)" << std::endl;
	}
	int fun()
	{
		return 1;
	}
private:
	~A()
	{
	}
};
template<class T>
T& m_delcavl() noexcept;
int main()
{
	using boost::typeindex::type_id_with_cvr;
    cout << type_id_with_cvr<decltype(m_delcavl<A>())>().pretty_name() << endl;
	cout << type_id_with_cvr<decltype(m_delcavl<A&>())>().pretty_name() << endl;
	cout << type_id_with_cvr<decltype(m_delcavl<A&&>())>().pretty_name() << endl;
    return 0;
}

在这里插入图片描述
我们自己写了一个返回左值引用的函数模板,注意看输出,
这里用到了引用折叠的知识。如果返回的是左值引用,我们要得到一个右值引用类型比较不好办,,但是如果我们返回的是右值引用,,我们是可以得到左值引用或者右值引用类型的,这里也是一个猜想,,所以标准库当中的这个函数模板返回的右值引用类型。或许可能还有其它考虑吧。

调用引用限定符修饰的成员函数示例

#include 
#include 
#include 
using namespace std;
class A
{
public:
	A(int i)
	{
		std::cout << "A(i)" << std::endl;
	}

	/*只能是左值对象调用*/
	int fun1() &
	{
		return 1;
	}
	/*只能是左值对象调用*/
	int fun2()&&
	{
		return 2;
	}
};
int main()
{
	/*一个临时对象,这里相当于是一个右值对象,所以只能调用fun2*/
	A(1).fun2();
    return 0;
}

推导函数返回值范例

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