std::declval
是一个模板函数, 它的作用是将T
替换成T&&
, 并且将T&&
作为返回对象.
目的是为了可以绕过一个对象的构造函数, 直接使用这个对象的成员函数,
通过配合decltype(expression)
可以在编译期就获取到这个对象的成员函数返回的类型.备注:
std::declval
不是一个关键字, 之所以标题加了个关键字, 是因为它很多时候都是跟decltype
一起用的.
不能当做常规模板函数使用, 必须配合 decltype(expression)
#include <iostream>
using std::cout;
using std::endl;
class Person {
private:
int age;
public: // constructors
Person(int a) : age(a) {};
public: // functions
int & getAge() {
cout << "getAge called" << endl;
return age;
};
};
int main(void) {
// 编译错误:
// 1. unresolved external symbol "class Person && __cdecl std::declval<class Person>(void)"
// (??$declval@VPerson@@@std@@YA$$QEAVPerson@@XZ) referenced in function main
//
// 2. fatal error LNK1120: 1 unresolved externals
std::declval<Person>();
return 0;
}
并没有真正运行函数.
如果运行了函数, 应该要输出: "getAge called", 但是实际运行结果中并没有.
也就是说 std::declval
并不是真正的要运行这个函数, 而是为了获取它的返回值声明的类型而已.
#include <iostream>
using std::cout;
using std::endl;
using std::declval;
using std::boolalpha;
using std::is_reference_v;
class Person {
private:
int age;
public: // constructors
Person(int a) : age(a) {};
public: // functions
int & getAge() {
cout << "getAge called" << endl;
return age;
};
};
int main(void) {
cout << boolalpha
<< is_reference_v<decltype(declval<Person>().getAge())>
<< endl;;
// output:
// true
return 0;
}
验证它的工作都是在编译期完成的
static_assert
是在编译期断言函数, 也就是说条件值如果是运行期生成的, 那么就无法通过编译.
#include <iostream>
using std::cout;
using std::endl;
using std::declval;
using std::boolalpha;
using std::is_reference_v;
class Person {
private:
int age;
public: // constructors
Person(int a) : age(a) {};
public: // functions
int & getAge() {
cout << "getAge called" << endl;
return age;
};
};
int main(void) {
// compile success!
constexpr bool is_ref = is_reference_v<decltype(declval<Person>().getAge())>;
static_assert(is_ref == true, "error");
return 0;
}
总结
std::declval
会在编译期完成对象的类型转换和调用, 这个调用不会直接调用函数, 而是期望拿到它的返回值类型.
配合 decltype(expression)
可以在编译期完成对值类型匹配的工作.