禁止使用返回非const引用的方法覆盖返回const引用的虚拟方法

时间:2020-08-25 09:59:39

标签: c++ c++14

以下代码编译:

struct Ret {};

struct A
{
    virtual const Ret& fun() = 0; 
};

struct B : public A
{
    Ret& fun() override
    {
        static Ret ret;
        return ret;
    }
};

int main()
{
    B b;
}

在编译期间,如何禁止使用不同的const说明符覆盖方法返回的引用,以用于返回类型?

谢谢。

1 个答案:

答案 0 :(得分:5)

以下所有标准参考文献均引用N4659: March 2017 post-Kona working draft/C++17 DIS


派生函数的返回类型必须与其重写的函数的返回类型协变,但反之则不必

[class.virtual]/7的约束[摘录,强调我的]:

覆盖函数的返回类型应或相同 到覆盖函数的返回类型或 covariant 与 函数的类。如果函数D​::​f覆盖了 函数B​::​f,如果以下情况,则函数的返回类型是协变的 它们满足以下条件:

  • [...]
  • (7.3)指针或引用具有相同的cv限定,并且返回类型D​::​f中的类类型具有相同的 cv-qualification小于或等于类别中的类类型 返回类型B​::​f

使以下程序格式正确

struct Ret {};

struct A {
    virtual const Ret& fun() = 0;
};

struct B : public A {
    Ret& fun() override { /* ... */ }
};

int main() {}

我们可能会注意到,底层A::fun对象的B接口的多态用法将强制该接口的返回类型保持不变,而以下程序格式不正确:

struct Ret {};

struct A {
    virtual Ret& fun() = 0;
};

struct B : public A {
    const Ret& fun() override { /* ... */ }
};

int main() { }

随附以下说明性编译器错误消息(Clang)

error: return type of virtual function 'fun' is 
       not covariant with the return type of the 
       function it overrides

这个要求很自然,因为我们可能会注意到,如果接口A if 可以多态调用非常量Ret&并返回fun(),即使派生对象通过返回const Ret&来实现重载,那么我们将有一种方法(通过多态性)修改const对象,这是未定义的行为。


有自然的解决方法(例如,将Curiosly Recurring Template Pattern和动态性多态性替换为注入基注入的派生类型中的常量声明),但这些方法似乎都可以解决an XY problem并可能实现模式只会增加代码的复杂性而没有任何明显的收获。