【问题标题】:MSVC: Covariant Return Types And Virtual InheritanceMSVC:协变返回类型和虚拟继承
【发布时间】:2017-10-21 01:28:15
【问题描述】:

我不确定这是 visual-c++ 编译器中的错误还是未定义的行为。

设置

struct DummyBase { virtual ~DummyBase() = default; };
struct DummyDerived : virtual public DummyBase {};

只是一个类和一个使用虚拟继承的派生类

DummyDerived derived;
DummyBase* base = &derived;

std::cout << "Derived : " << &derived << std::endl;
std::cout << "Base    : " << base << std::endl;

DummyDerived* 转换为DummyBase* 时,指针偏移。这似乎是由虚拟继承引起的:

Derived : 00000000002CF838
Base    : 00000000002CF840

即使指针值不同,比较也会返回 true:

std::cout << "IsSame  : " << (base == &derived) << std::endl << std::endl;

输出:

IsSame  : 1

到目前为止一切顺利。

问题

问题出现在以下设置中:

 struct IBaseReturner
 {
   virtual DummyBase* Get() = 0;
 };

 struct IDerivedReturner : public virtual IBaseReturner
 {
   virtual DummyDerived* Get() = 0;
 };

 struct BaseReturner : public virtual IBaseReturner
 {
 };

 struct DerivedReturner : public BaseReturner, public virtual IDerivedReturner
 {
   DummyDerived* Ptr;
   virtual DummyDerived* Get() override { return Ptr; }
 };

这里我们有类的接口和实现,其方法返回DummyBaseDummyDerived,通过协变返回类型覆盖。再次使用虚拟继承。

// Setup
DerivedReturner returner;
returner.Ptr = &derived;
IBaseReturner* baseReturner = &returner;

现在将DummyDerived*DerivedReturnerDummyBase*从同一个返回者转换为IBaseReturner

DummyDerived* derivedOriginal = returner.Get();
DummyBase* baseFromInterface = baseReturner->Get();

像上面一样比较:

std::cout << "Derived Original    : " << derivedOriginal << std::endl;
std::cout << "Base from Interface : " << baseFromInterface << std::endl;

输出

Derived Original    : 00000000002CF838
Base from Interface : 00000000002CF838

与上面不同,指针具有相同的值。 现在比较它们:

std::cout << "IsSame  : " << (baseFromInterface == derivedOriginal) << std::endl;

输出:

IsSame  : 0

比较返回false,即使地址相同。这是意料之中的,因为指向DummyBase 的指针应该具有不同的值。 同样在尝试进行 dynamic_cast 时:

std::cout << dynamic_cast<DummyDerived*>(baseFromInterface);

并且抛出异常:

unknown file: error: C++ exception with description "Access violation - no RTTI data!" thrown in the test body.

显然是因为指针没有正确偏移。

结论

似乎在调用IBaseReturner::Get 时,visual-c++ 编译器未能执行必要的指针运算以将DummyDerived* 转换为DummyBase*。这发生在 vs2013 和 vs2015 中(没有尝试任何其他版本)。使用 gcc 编译时也可以正常工作。

问题

虽然设置可能有点复杂,但问题却相当简单:

这是一个 msvc 错误还是我导致了未定义的行为?

添加了一个在线示例:http://rextester.com/KHZXGQ27304

【问题讨论】:

  • 我很惊讶IBaseReturner* baseReturner = &amp;returner; 编译;在returner 中有两个不同的IBaseReturner 实例。我可以想象 C++ 标准会采用任何一种方式(一条路径更短),但我预计该行会出现错误。
  • struct DerivedReturner : public BaseReturner, public virtual DerivedReturner 那不编译,也不可能。你有一个从它自己派生的类。我假设你的意思是从IDerivedReturner
  • @Yakk IBaseReturner 是一个虚拟基类;事实上,DerivedReturner 中只有一个 IBaseReturner 实例,这使得演员阵容明确。
  • @igor 它只有一条路径是虚拟的。这不是实际的代码吗?需要minimal reproducible example,而不是伪代码。投票结束,直到 OP 发布一个最小且准确的示例,其中包含一个证明代码准确的链接。我怀疑实际的代码可能涉及不同的恶作剧,但很难在我的手机上重新输入,结果我也必须修复它
  • @Yakk 你是什么意思,只有一条路径是虚拟的?没有一个地方可以非虚拟继承IBaseReturnerHere's an MCVE 。对我来说,这看起来像是一个 MSVC 错误。

标签: c++ visual-c++ visual-studio-2013 virtual-inheritance covariant-return-types


【解决方案1】:

您的代码没有未定义的行为(GCC 和 Clang 都可以正常编译和执行,演示:https://gcc.godbolt.org/z/q91vbhjTh),并且由于 MSVC 中的错误而导致其运行失败,该错误仍然存​​在于 Visual Studio 2019 和 2022 中.

报告的 MSVC 错误:https://developercommunity.visualstudio.com/t/Invalid-code-generated-for-virtual-inher/1598214

来自他们的回复:

这与编译器中虚函数表布局的一些问题有关,涉及到虚继承和协变返回类型。 如果您被阻止,一种可能的解决方法是交换基类的顺序:

struct DerivedReturner : public virtual IDerivedReturner, public BaseReturner

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 2014-11-20
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2013-12-07
    相关资源
    最近更新 更多