GCC与Clang动态_cast行为差异解析:继承权限的影响
GCC与Clang对C++虚继承和dynamic_cast的行为差异分析
示例代码
#include <iostream> class Body { public: virtual ~Body() { std::cout << "Body::~Body()" << std::endl; } }; class HydrophobicObject : public virtual Body { }; class ControlledObject : public virtual Body { }; class CrawlingObject : public virtual ControlledObject { }; class OwnerProtection { public: void f() { std::cout << "OwnerProtection::f" << std::endl; } }; class Caterpillar : public CrawlingObject, public OwnerProtection, HydrophobicObject { }; int main(int argc, char *argv[]) { Caterpillar caterpillar; Body& body = caterpillar; auto p = dynamic_cast<OwnerProtection&>(body); p.f(); return 0; }
问题现象
- 使用Apple clang 15.0.0编译运行时,程序正常输出
OwnerProtection::f,不会抛出bad_cast异常; - 使用GCC编译运行时,会触发
bad_cast异常; - 若给
Caterpillar继承HydrophobicObject时添加public关键字(即class Caterpillar : public CrawlingObject, public OwnerProtection, public HydrophobicObject),GCC也能正常执行。
为什么public关键字影响GCC的行为?
这源于GCC对C++标准中dynamic_cast访问控制规则的实现细节:
- C++中类的默认继承权限为private,原代码中
Caterpillar是以私有继承方式继承HydrophobicObject的; - GCC的实现逻辑认为,当虚基类
Body存在多条继承路径到Caterpillar时,若其中一条路径(Body <- HydrophobicObject <- Caterpillar)是私有继承,会导致外部上下文(如main函数)无法完整访问对象的继承结构信息,进而无法确认Body&引用的对象包含OwnerProtection子对象,因此触发bad_cast; - 当将
HydrophobicObject的继承改为public后,所有从Body到Caterpillar的路径都变为可访问,GCC可以正常遍历继承结构、确认OwnerProtection子对象的存在,转换自然成功。
哪款编译器的行为符合C++标准?
根据C++标准的相关规定:
- 对于交叉类型的
dynamic_cast(从一个基类引用转换到同一对象的另一个基类引用),只要目标基类是完整对象类型的可访问基类,且源基类到完整对象的路径可访问,转换就应当成功; - 标准明确指出,基类的可访问性不影响
dynamic_cast的格式合法性,但转换成功的前提是目标类型相对于完整对象是可访问的。
回到示例代码:
OwnerProtection是Caterpillar的public基类,属于可访问范畴;Body到Caterpillar的路径中,Body <- ControlledObject <- CrawlingObject <- Caterpillar这条public继承路径完全可访问,即便存在另一条私有继承路径,也不影响确认对象的完整类型及OwnerProtection子对象的存在。
因此,Clang的行为符合C++标准,GCC的行为属于实现层面的严格化处理,不符合标准要求。
内容的提问来源于stack exchange,提问作者John Tracid
相关产品推荐
相关产品推荐

