从标准库容器继承会导致切片

Posted

技术标签:

【中文标题】从标准库容器继承会导致切片【英文标题】:Inheriting from standard library containers results in slicing 【发布时间】:2020-10-09 12:22:39 【问题描述】:

我遇到了一个旧的SO post about inheriting from std::vector。我不清楚其中一个 cmets,为什么当您从标准库容器继承并使用指向基的指针/引用时会发生切片?

Godbolt

#include <vector>
#include <iostream>

using namespace std;

class MyVec : public vector<int>
;

void printTypeBase(vector<int>& vecObj)

    cout << "base parameter ref: " << typeid(vecObj).name() << endl;


void printTypeDerived(MyVec& vecObj)

    cout << "derived parameter ref: " << typeid(vecObj).name() << endl;



int main()

    auto myVec = MyVec;

    printTypeBase(myVec);
    printTypeDerived(myVec);

输出:

基本参数参考:St6vectorIiSaIiEE

派生参数参考:5MyVec


当我尝试在 printTypeBase 内进行动态转换时:

auto temp = dynamic_cast<MyVec&>(vecObj);

我收到一条明确的错误消息:

错误:'std::vector' 不是多态的


回想起来,我的问题应该是为什么std::vector 不是多态类型?

【问题讨论】:

这里没有进行切片,因为使用了指向引用的指针。在任何地方用class foo 替换std::vector&lt;int&gt;,你会得到相同的结果。 C++ 中的类型不是动态的,它们是固定的和静态的。在printTypeBase 函数中,vecObj 变量是对vector&lt;int&gt; 的引用,编译器无法更改它。 这不是切片。 MyVec 是一个空的派生类。里面没有什么可以切的。如果要查看切片,则添加一个数据成员,例如 int,然后通过 vector&lt;int&gt;&amp; 复制对象。然后你得到int成员的切片,因为vector&lt;int&gt;::operator=int成员一无所知。 @SamVarshavchik when I replace vector<int> with a foo class 其中包含虚函数,我得到“正确”行为,typeid 为这两个函数打印“MyVec”。我现在明白了关于非多态类型的意义,所以这是有道理的。 即使使用虚函数,仍然不会发生切片。它仅在基类被复制 时发生。不进行复制。 typeid 并不表示对象切片正在发生。 【参考方案1】:

标准库容器没有虚拟方法,也没有虚拟析构函数。他们完全没有 vtable。

您的代码无法测试您想要测试的内容。

【讨论】:

【参考方案2】:

但是,对于非多态类型的引用(例如没有虚拟方法的类),这里不会发生切片,typeid 解析为参数的静态类型信息,而不是动态类型。

【讨论】:

【参考方案3】:

为什么会出现切片

您的示例中没有切片。与从任何其他类型继承相比,标准库容器没有什么特别会导致切片。

基本参数参考:St6vectorIiSaIiEE

错误:'std::vector' 不是多态的

后一个错误解释了typeid 的结果。 Vector 不是多态类型。这是因为它没有虚函数。 dynamic_cast 只允许用于多态类型,typeid 仅在引用多态类型时才解析为动态类型。

【讨论】:

以上是关于从标准库容器继承会导致切片的主要内容,如果未能解决你的问题,请参考以下文章

STL标准库-容器适配器

一文掌握使用 Go 标准库 sort 对切片进行排序

通过继承扩展 C++ 标准库?

为啥在 C++ 20 中从标准库容器中删除了比较运算符?

C++标准库

标准模板库使用参考——vector向量容器