C++关键字 declval

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) 可以在编译期完成对值类型匹配的工作.

 

参考

std::declval
Is only “void” in non-referenceable type?

©著作权归作者所有,转载或内容合作请联系作者
平台声明:文章内容(如有图片或视频亦包括在内)由作者上传并发布,文章内容仅代表作者本人观点,简书系信息发布平台,仅提供信息存储服务。

推荐阅读更多精彩内容