首页 > 解决方案 > 不允许用返回非常量引用的方法覆盖返回常量引用的虚方法

问题描述

以下代码编译:

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 说明符作为返回类型的覆盖方法返回引用?

提前致谢。

标签: c++c++14

解决方案


以下所有标准参考均指N4659:2017 年 3 月 Kona 后工作草案/C++17 DIS


派生函数的返回类型需要与它覆盖的函数的返回类型协变,反之则不然

[class.virtual]/7 [摘录,强调我的] 管理:

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

  • [...]
  • (7.3) 指针或引用都具有相同的 cv 限定,并且 的返回类型中的类类型具有与 的返回类型中的类类型D​::​f 相同或更少的cv 限定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允许多态调用非 const Ret&-return fun(),即使派生对象实现重载作为返回 a const Ret&,那么我们将有一种方法修改const对象(通过多态),即未定义的行为。


有自然的变通方法(例如,用好奇的循环模板模式替换动态多态性和对 in -base 注入派生类型的 constness 断言),但可以说这些似乎都解决了 XY 问题,并且可能实现只会增加复杂性的模式代码没有任何明显的收获。


推荐阅读