为什么std :: type_info是多态的?

Dou*_*oug 16 c++ polymorphism rtti typeinfo

是否有理由std::type_info指定为多态?析构函数被指定为虚拟(并且在"C++的设计和演变"中对"因此它是多态的"效果进行了评论).我真的没有看到令人信服的理由.我没有任何具体的用例,我只是想知道它背后是否有任何理由或故事.


以下是我提出并拒绝的一些想法:

  1. 这是一个扩展点-实现可能定义子类,然后程序可能会尝试dynamic_cast一个std::type_info到另一个,实现定义派生类型.这可能是原因,但似乎实现添加实现定义的成员(可能是虚拟的)同样容易.希望测试这些扩展的程序无论如何都必然是不可移植的.
  2. 这是为了确保在delete使用基指针时正确销毁派生类型.但目前还没有标准的派生类型,用户不能定义有用的派生类型,因为type_info没有标准的公共构造函数,所以delete荷兰国际集团一个type_info指针是从未合法和便携.并且派生类型没有用,因为它们无法构造 - 我知道这种不可构造的派生类型的唯一用途是在is_polymorphic类型特征之类的实现中.
  3. 它保留了具有自定义类型的元类的可能性 - 每个真正的多态class A都会得到派生的"元类" A__type_info,它来源于type_info.也许这样的派生类可以暴露new A以类型安全的方式调用各种构造函数参数的成员,以及类似的东西.但type_info实际上制作多态本身实际上使得这样的想法基本上无法实现,因为你必须为你的元类创建元类,无限制,如果所有type_info对象都有静态存储持续时间,这就是一个问题.也许禁止这是使其变为多态的原因.
  4. 有一些用于将RTTI功能(除了dynamic_cast)应用于std::type_info自身,或者有人认为它很可爱,或者如果type_info不是多态的则令人尴尬.但鉴于没有标准派生类型,并且标准层次结构中没有其他类可以合理地尝试交叉投射,问题是:什么?是否有用于表达式typeid(std::type_info) == typeid(typeid(A))
  5. 这是因为实施者将创建他们自己的私有派生类型(我相信GCC会这样做).但是,为什么还要指定它呢?即使析构函数未被指定为虚拟,并且实现者决定它应该是,但是实现可以将其声明为虚拟,因为它不会更改允许的操作集type_info,因此便携式程序将无法分辨其中的不同之处.
  6. 这与部分兼容的ABI共存的编译器有关,可能是动态链接的结果.type_info如果type_info保证是虚拟的,也许实现者可以以可移植的方式识别他们自己的子类(而不是来自另一个供应商的子类).

最后一个对我来说是最合理的,但它相当弱.

Arm*_*yan 9

C++标准表示typeid返回type_info类型的对象,或者它的一个IMPLEMENTATION-DEFINED子类.所以...我想这几乎就是答案.所以我不明白为什么你拒绝你的第1点和第2点.

第5.2.8段当前C++标准第1条规定:

typeid表达式的结果是静态类型const std :: type_info(18.5.1)和动态类型const std :: type_info或const name的左值,其中name是从std :: type_info派生的实现定义类,它保留了18.5.1.61中描述的行为)左值引用的对象的生命周期延伸到程序的末尾.是否在程序末尾为type_info对象调用析构函数是未指定的.

这反过来意味着可以编写以下代码是合法的和正常的: const type_info& x = typeid(expr);这可能要求type_info是多态的


Ste*_*sop 9

我认为这是为了方便实施者.它允许它们定义扩展type_info类,并通过指向type_info程序出口的指针删除它们,而不必构建特殊的编译器魔法来调用正确的析构函数,或以其他方式跳过箍.

当然,实现可以将其声明为虚拟,因为它不会更改type_info上允许的操作集,因此便携式程序无法区分它们.

我认为这不是真的.考虑以下:

#include <typeinfo>

struct A {
    int x;
};

struct B {
    int x;
};

int main() {
    const A *a1 = dynamic_cast<const A*>(&typeid(int));
    B b;
    const A *a2 = dynamic_cast<const A*>(&b);
}
Run Code Online (Sandbox Code Playgroud)

无论是否合理,都允许第一个动态转换(并且计算为空指针),而不允许第二个动态转换.因此,如果type_info在标准中定义了默认的非虚拟析构函数,但是实现添加了一个虚拟析构函数,那么便携式程序可以区分[*].

将虚拟析构函数放入标准中似乎比我更简单:

a)在标准中加注,虽然类定义暗示type_info没有虚函数,但允许有虚拟析构函数.

b)确定可以区分是否type_info是多态的程序集,并禁止所有程序.它们可能不是非常有用或高效的程序,我不知道,但是为了禁止它们,你必须提出一些标准语言来描述你对正常规则所做的具体例外.

因此,我认为标准必须要么强制虚拟析构函数,要么禁止它.使其成为可选项太复杂了(或者我应该说,我认为它将被判断为不必要的复杂.复杂性从未阻止标准委员会在其认为值得的领域......)

但是,如果它被禁止,那么实施可以:

  • 将虚拟析构函数添加到某个派生类中 type_info
  • 获得所有所属类别的对象从
  • 在内部使用它作为一切的多态基类

这将解决我在帖子顶部描述的情况,typeid表达式的静态类型仍然是const std::type_info,因此实现很难定义程序可以dynamic_cast到各种目标的扩展,以查看type_info它们所具有的对象类型一个特例.也许标准希望允许这样做,尽管实现总是可以提供typeid具有不同静态类型的变体,或者保证static_cast某个扩展类可以工作,然后让程序dynamic_cast从那里开始.

总之,据我所知,虚拟析构函数可能对实现者有用,并且删除它不会获得任何除此之外的任何东西我们不会花时间想知道为什么它在那里;-)

[*]实际上,我没有证明这一点.我已经证明,在其他条件相同的情况下,非法程序会编译.但是,通过确保所有不相等并且不能编译,实现可能可以解决这个问题.Boost is_polymorphic是不可移植的,所以虽然程序可以测试一个类多态的,但应该是,一致的程序可能没有办法测试一个类不是多态的,那不应该多态的.我认为即使这是不可能的,但要证明,为了从标准中删除一行,需要付出相当大的努力.