You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

为何抽象类可作为容器类型的函数参数?模板何时实例化?

抽象类作为std::vector元素:编译通过的原因与模板实例化时机

先看问题中的代码:

#include <vector>
#include <iostream>

class Base{
    public:
    virtual ~Base() = default;
    Base(const Base&) = default;
    Base(Base&&) = default;
    Base &operator=(const Base&) = default;
    Base &operator=(Base&&) = default;
    virtual void Method() = 0;
    int m_data{0};
};

class Derived : public Base{
    public:
    void Method() override{}
};


void func(std::vector<Base> &vec){
    int var = vec[0].m_data;//this compiles
    //auto base = vec[0]; //this does not compile
}

int main(){}

一、为什么std::vector<Base>的函数声明能通过编译?

C++模板遵循延迟实例化规则:模板的具体代码不会在声明阶段生成,只有当代码实际需要使用模板的某个成员(或生成模板的具体实例)时,编译器才会进行实例化操作。

这段代码里,func只是声明了参数是std::vector<Base>&,此时编译器不需要生成std::vector<Base>的任何实现代码,自然不会去检查Base是否可实例化。抽象类的限制是不能创建其实例对象,但仅仅声明一个指向该类型容器的引用,完全不涉及对象创建,所以编译可以通过。

二、为什么vec[0].m_data能编译,而auto base = vec[0]不行?

  • vec[0]返回的是Base&(抽象类的引用),C++允许抽象类的引用和指针存在(这正是抽象类设计的核心目的之一:通过基类指针/引用实现多态调用)。访问引用指向对象的成员m_data,只是通过引用寻址读取内存,不需要创建新的Base对象,因此编译合法。
  • auto base = vec[0]本质是执行拷贝构造:把vec[0](Base&)拷贝到新的Base对象base中。这时候必须创建Base的实例,但Base是抽象类(包含纯虚函数),C++明确禁止创建抽象类的对象,因此这行代码会触发编译错误——它强制编译器尝试实例化Base的拷贝构造函数,而这违反了抽象类的规则。

三、std::vector<Base>什么时候会被实例化?

当代码的操作需要编译器生成std::vector<Base>的具体实现代码时,模板就会被实例化,此时编译器会检查Base是否满足std::vector的元素要求(即可构造、可拷贝等),这时候就会因为Base是抽象类而报错。常见的触发场景包括:

  • 创建std::vector<Base>对象并执行需要实例化元素的操作:比如声明std::vector<Base> vec;后调用vec.resize(5)(需要默认构造Base对象)、vec.push_back(Derived{})(需要将Derived切片为Base,即拷贝构造Base),都会触发实例化并报错。
  • 执行涉及创建Base实例的容器操作:比如前面提到的auto base = vec[0],需要拷贝构造Base对象,此时会触发编译器检查Base是否可实例化。
  • 实际调用func函数且函数内操作依赖vector成员的实例化:比如如果func里写了vec.push_back(Derived{}),当func被调用时,编译器会实例化vector的push_back成员,进而检查Base是否可拷贝构造,最终报错。

内容的提问来源于stack exchange,提问作者orlandini

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.07.10 15:19:58