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

为何virtual关键字会导致C++代码编译失败?

为何无继承类中添加virtual关键字会引发编译错误?

问题背景

在无继承关系的类中,virtual关键字本不应产生显著影响,但在以下代码中,给Bamboozle类的foo重载添加virtual后,编译直接失败;移除virtual则可正常编译。我们需要拆解背后的机制。

代码示例

#define USE_VIRTUAL 1
// 编译命令: cl /W4 /EHsc main.cpp /link /out:main.exe
#include<iostream>
#include<memory>

struct Dummy {int i = 3;};
class Uncopyable {
    public:
        // 因包含不可拷贝成员,隐式删除拷贝构造函数
        std::unique_ptr<Dummy> m_innerPtr;

        // 虚析构函数,用于支持正确继承
        // 注:虚析构不会隐式删除拷贝构造/赋值,但会隐式删除移动构造/赋值
        virtual ~Uncopyable() = default;

        // 可访问的构造函数
        Uncopyable() = default;
        Uncopyable(std::unique_ptr<Dummy> dummy):m_innerPtr(std::move(dummy)){};

};


template <typename MaybeCopyable>
class Bamboozle {
    public: 
    void foo(std::shared_ptr<MaybeCopyable> obj) {
        std::cout << "Reached Bamboozle::foo(shared_ptr)" << std::endl;
    }

    // 按值传参的重载版本
#if USE_VIRTUAL
    virtual
#endif
    void foo(MaybeCopyable obj){
        std::cout << "Reached Bamboozle::foo(Uncopyable)" << std::endl;
        foo(std::make_shared<Uncopyable>(std::move(obj)));
    }
};



int main(int argc, char** argv){
    // 构造参数对象
    std::shared_ptr<Uncopyable> uncSharedPtr = std::make_shared<Uncopyable>();

    // 调用的是foo(shared_ptr)版本
    Bamboozle<Uncopyable> bamboozleObj;
    bamboozleObj.foo(uncSharedPtr);
    return 0;
};

编译错误信息

cl /W4 /EHsc main.cpp /link /out:main.exe
Microsoft (R) C/C++ Optimizing Compiler Version 19.38.33134 for x64
Copyright (C) Microsoft Corporation.  All rights reserved.

main.cpp
main.cpp(46): warning C4100: 'argv': unreferenced formal parameter
main.cpp(46): warning C4100: 'argc': unreferenced formal parameter
C:\Program Files\Microsoft Visual Studio\2022\Community\VC\Tools\MSVC\14.38.33130\include\xutility(255): error C2280: 'Uncopyable::Uncopyable(const Uncopyable &)': attempting to reference a deleted function
main.cpp(24): note: compiler has generated 'Uncopyable::Uncopyable' here
main.cpp(24): note: 'Uncopyable::Uncopyable(const Uncopyable &)': function was implicitly deleted because a data member invokes a deleted or inaccessible function 'std::unique_ptr<Dummy,std::default_delete<Dummy>>::unique_ptr(const std::unique_ptr<Dummy,std::default_delete<Dummy>> &)'
C:\Program Files\Microsoft Visual Studio\2022\Community\VC\Tools\MSVC\14.38.33130\include\memory(3319): note: 'std::unique_ptr<Dummy,std::default_delete<Dummy>>::unique_ptr(const std::unique_ptr<Dummy,std::default_delete<Dummy>> &)': function was explicitly deleted
C:\Program Files\Microsoft Visual Studio\2022\Community\VC\Tools\MSVC\14.38.33130\include\xutility(255): note: the template instantiation context (the oldest one first) is
main.cpp(51): note: see reference to class template instantiation 'Bamboozle<Uncopyable>' being compiled
main.cpp(38): note: while compiling class template member function 'void Bamboozle<Uncopyable>::foo(MaybeCopyable)'
        with
        [
            MaybeCopyable=Uncopyable
        ]
main.cpp(40): note: see reference to function template instantiation 'std::shared_ptr<Uncopyable> std::make_shared<Uncopyable,Uncopyable>(Uncopyable &&)' being compiled
C:\Program Files\Microsoft Visual Studio\2022\Community\VC\Tools\MSVC\14.38.33130\include\memory(2769): note: see reference to function template instantiation 'std::_Ref_count_obj2<_Ty>::_Ref_count_obj2<_Ty>(_Ty &&)' being compiled
        with
        [
            _Ty=Uncopyable
        ]
C:\Program Files\Microsoft Visual Studio\2022\Community\VC\Tools\MSVC\14.38.33130\include\memory(2094): note: see reference to function template instantiation 'void std::_Construct_in_place<_Ty,_Ty>(_Ty &,_Ty &&) noexcept(false)' being compiled
        with
        [
            _Ty=Uncopyable
        ]

背后机制拆解

1. 模板函数实例化的规则差异

  • 无virtual时:main函数中调用的是foo(std::shared_ptr<Uncopyable>),编译器发现按值传参的foo(Uncopyable)从未被调用,因此不会实例化这个重载函数。哪怕该函数内部有问题,也不会触发编译错误。
  • 有virtual时:根据C++标准,模板类的虚成员函数必须被实例化——因为编译器需要为类生成虚函数表,而虚函数表中必须包含该函数的地址。因此,编译器会强制实例化Bamboozle<Uncopyable>::foo(Uncopyable)。

2. Uncopyable类的构造函数状态

Uncopyable的构造函数有两个关键特性:

  • 拷贝构造函数被删除:因为类中包含std::unique_ptr成员,而std::unique_ptr的拷贝构造函数是显式删除的,因此编译器会隐式删除Uncopyable的拷贝构造函数。
  • 移动构造函数被删除:因为用户声明了虚析构函数(virtual ~Uncopyable() = default;),根据C++11及以后的规则,只要用户显式声明了析构函数,编译器就不会自动生成移动构造函数和移动赋值运算符。而代码中也没有手动定义移动构造函数,因此Uncopyable没有可用的移动构造函数。

3. 实例化后的错误触发

当foo(Uncopyable)被强制实例化时,其中的代码std::make_shared<Uncopyable>(std::move(obj))会被编译:

  • std::make_shared尝试通过Uncopyable&&(右值)构造对象,但Uncopyable没有移动构造函数。
  • 编译器会尝试回退到使用拷贝构造函数,但拷贝构造函数已经被删除。
  • 最终触发C2280错误:尝试引用已删除的拷贝构造函数。

总结

本质是virtual关键字强制触发了原本不会被实例化的模板成员函数,而该函数内部的代码暴露了Uncopyable类缺少有效构造函数(移动/拷贝均不可用)的问题。如果没有virtual,这个未被调用的函数不会被实例化,错误也就不会显现。

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.06.22 18:44:53