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函数的返回值类型。
注意,并没有调用类A的构造函数。
我们来分析下标准库当中的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;
}
在上面的例程当中,增加了一个私有的构造函数,
并且增加了一个返回类型本身的函数模板,但是编译时报错了,原因就是这里会生成一个临时对象,导致无法调用析构函数报错了,,如果把那个函数模板改为左值引用或者右值引用,你会发现编译时就不会报错了,这里就是在这种情况下说明了返回类型本身是不好的。那返回左值引用和右值引用都可以,为什么标准库的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;
}