RTTI & decltype & declval

ri# RTTI
Run-Time Type Information,运行时类型信息。

RTTI

这种情况,一般是运行时的类型转换的,一般是和类型转换配合使用,给出代码实例:

#include <iostream>

class Base {
public:
    virtual void foo() {
        std::cout << "base derived\n";
    }
};

class Derived: public Base {
public:
    void foo() override {
        std::cout << "derived foo\n";
    }
};

int main() {
    Base* bptr = dynamic_cast<Base*>(new Derived());
    bptr->foo();
}

bptr虽然是Base*类型,但是 RTTI 让bptr 成为了Derived 类型。

decltype

RTTI 机制说明了C++会有运行时的类型。而在模板编程中,我们传入的类型必须是固定的,但是 RTTI 无法满足这个要求,因为RTTI 的类型必须得运行的时候确定。为了解决这种推导的问题,C++引入auto&decltype机制。auto不再赘述,这里主要说明decltype 如何在编译期间确定 RTTI。

decltype(expr) 是最基本的用法,即expr 如果是普通类型,那么直接返回原样的类型;如果是表达式,则返回表达式计算结果的类型。举个例子:

#include <iostream>

int foo() {
    std::cout << "foo\n";
    return 0;
}

int main() {
    int a = 10;
    decltype(a) b = a;
    decltype(foo()) c = a;
    // 注意 lambda 无法直接推导,必须要赋值,然后执行表达式才可以
    auto F = []() { return 0; };
    decltype(F()) d = a;

    std::cout << a << ", " << b << ", " << c << ", " << d << std::endl;
    return 0;
}

上面的表达式虽然调用了执行的符号,但是不会执行,只用用于获取 RTTI

decltype(e) 有几个规则:

  • 如果e是一个没有带括号的标记符表达式或者类成员访问表达式,那么的decltype(e)就是e所命名的实体的类型。此外,如果e是一个被重载的函数,则会导致编译错误。
  • 否则 ,假设e的类型是T,如果e是一个将亡值,那么decltype(e)为T&&
  • 否则,假设e的类型是T,如果e是一个左值,那么decltype(e)为T&。
  • 否则,假设e的类型是T,则decltype(e)为T。

进一步拓展一个例子:

#include <iostream>

struct Foo {
    int a{10};

    int foo() {
        return 0;
    }
};

int main() {
    int a = 10;
    decltype(a) b = 10;
    decltype((b)) c = b;  // (1)
    c = 20;
    std::cout << "b = " << b << ", c = " << c << std::endl;

    decltype(Foo().a) d = 30;
    decltype((Foo().a)) e = 40;  // (2)
    std::cout << "d = " << d << ", e = " << e << std::endl;

    decltype(Foo().foo()) f = 100;  // (3)
    std::cout << "f = " << f << std::endl;
    return 0;
}
/*
b = 20, c = 20
d = 30, e = 40
f = 100
*/

(1)中,c 表示引用类型, (2)中的 e 表示右值类型,(3)中的 f 也是左值,注意使用方式,Foo::foo 不会真正执行调用。

decltype(auto) 的使用方式,给出一个最典型的应用:

template<typename Container, typename Index>
decltype(auto)authAndAccess(Container&& c, Index i){
    authenticateUser();
    return std::forward<Container>(c)[i];
}

返回值如果是左值,那么就作为左值;右值当做右值。

declval

declval把所有的类型转换成左值引用,一般和decltype 配合使用,用于转换那些没有构造函数的类型,给个代码实例:

#include <iostream>
#include <utility>

struct Foo {
    Foo() = delete;
    int foo() {
        return 0;
    }
};

int main() {
    decltype(std::declval<Foo>().foo()) a = 10;
    std::cout << a << std::endl;
    return 0;
}

Foo 由于没有构造函数,所有不能直接decltype(Foo().foo()),只能和std::declval配合使用,std::declval<Foo>()返回一个Foo&

参考文档

https://www.cnblogs.com/QG-whz/p/4952980.html
https://zsmj2017.tech/post/77bd3be8.html

上一篇:C++ Windows下dll的使用


下一篇:exe调用DLL的方式