基类含unique_ptr时,引用类型dynamic_cast为何编译失败?
问题分析:dynamic_cast引用版编译失败的原因与解决办法
问题场景
基类包含接收基类引用的纯虚函数(用于拷贝逻辑),派生类中尝试将该引用转为派生类类型,原本正常;但在基类添加Base类型的unique_ptr成员后,引用类型的dynamic_cast编译失败,改用指针类型则正常。注释掉unique_ptr成员或引用转换的代码时,程序可正常编译运行。
示例代码
#include <memory> #include <iostream> class Base { public: Base() {} void hello() const { std::cout << "hello world" << std::endl; } protected: virtual void cvt( Base& ) const = 0; std::unique_ptr<Base> any_member_unique_ptr; // "A" }; class Derived : public Base { public: Derived() { std::cout << "new Derived" << std::endl; } void cvt( Base& in ) const override { const auto cast_fail = dynamic_cast<const Derived&>( in ); // "B" const auto cast_okay = dynamic_cast<const Derived*>( &in ); hello(); } }; int main(int argc, char *argv[]) { Derived D, P; D.cvt( P ); }
编译错误信息
tt.cpp: In member function ‘virtual void Derived::cvt(Base&) const’: tt.cpp:17:69: error: use of deleted function ‘Derived::Derived(const Derived&)’ 17 | const auto cast_fail = dynamic_cast<const Derived&>( in ); // "B" | ^ tt.cpp:13:7: note: ‘Derived::Derived(const Derived&)’ is implicitly deleted because the default definition would be ill-formed: 13 | class Derived : public Base { | ^~~~~~~ tt.cpp:13:7: error: use of deleted function ‘Base::Base(const Base&)’ tt.cpp:4:7: note: ‘Base::Base(const Base&)’ is implicitly deleted because the default definition would be ill-formed: 4 | class Base { | ^~~~ tt.cpp:4:7: error: use of deleted function ‘std::unique_ptr<_Tp, _Dp>::unique_ptr(const std::unique_ptr<_Tp, _Dp>&) [with _Tp = Base; _Dp = std::default_delete<Base>]’ In file included from /usr/include/c++/11/memory:76, from tt.cpp:1: /usr/include/c++/11/bits/unique_ptr.h:468:7: note: declared here 468 | unique_ptr(const unique_ptr&) = delete; | ^~~~~~~~~~ make: *** [<builtin>: tt] Error 1
原因解析
- 引用转换的隐式拷贝陷阱:
dynamic_cast<const Derived&>(in)返回的是引用,但你写的const auto cast_fail = ...中,auto会推导为const Derived(值类型),这意味着代码试图用转换后的引用拷贝构造一个新的Derived对象。 - 拷贝构造被删除的连锁反应:基类中的
std::unique_ptr<Base>成员的拷贝构造函数是被显式删除的,因此基类的默认拷贝构造函数会被隐式删除;派生类继承了这一特性,其默认拷贝构造函数也会被隐式删除。当代码尝试拷贝构造Derived对象时,就会触发编译错误。 - 指针转换无拷贝行为:指针版的
dynamic_cast返回的是指针,const auto cast_okay推导为const Derived*,只是赋值指针地址,不需要拷贝构造对象,因此不会触发错误。
解决方案
将引用转换的结果声明为引用类型,避免触发拷贝构造:
// 方式1:显式声明引用类型 const Derived& cast_ok = dynamic_cast<const Derived&>(in); // 方式2:用auto&推导引用类型 const auto& cast_ok = dynamic_cast<const Derived&>(in);
修改后,代码只是将引用绑定到原对象上,不会触发拷贝构造,即可正常编译运行。
内容的提问来源于stack exchange,提问作者rand'Chris
相关产品推荐
相关产品推荐

