是否可以阻止引用的向上转换

Is it possible to block up-casting of references?

本文关键字:转换 引用 是否      更新时间:2023-10-16

考虑一个用例:

  • 你有一个可复制的类Base,你并不真正关心它会发生什么。
  • 从它公开继承是一个Derived类,它不应该转换为Base。一点也不。甚至没有对它的引用,对Base的引用,换句话说,隐式绑定应该是非法的:Derived& -> Base&

注意:Derived类是可复制的,只是它的内部不应该被放入常规的Base对象中。由于可以直接禁止Derived初始化Base,因此问题仍然是是否可以禁止编译绕过它:Derived& -> Base& -> Base .

假设static_cast,指针不是问题 - 只是函数调用中的自动绑定。

下面是显示问题的基本示例:

#include <iostream>
using namespace std;
class Derived;
class Base
{
public:
    Base(int var)
        : m_var(var)
    {
        std::cout << "Base default ctor with value: " << m_var << std::endl;
    }
    Base& operator=(const Derived&) = delete;
    Base& operator=(Derived&&) = delete;
    Base(const Derived&) = delete;
    Base(Derived&&) = delete;
    int m_var;
};
class Derived : public Base
{
public:
    Derived(int var)
        : Base(var)
    {
        std::cout << "Derived default ctor with value: " << m_var << std::endl;
    }
    Base unprotect() const
    {
        std::cout << "Derived unprotected with value: " << m_var << std::endl;
        return Base(m_var);
    }
};
void foo(Base& base)
{
    std::cout << "foo with value: " << base.m_var << std::endl;
    // Base b2 = base; // just copied Derived, goal is to prohibit it!
}
int main()
{
    Base b1(1);
    foo(b1);
    Derived d1(2);
    foo(d1);            // is it at all possible to disallow implicit Derived& -> Base&?
    // rationale is to require explicit: Base& Dervied::getBaseRef()    
    // Base b2 = d1; // illegal: error: use of deleted function 'Base::Base(const Derived&)'
    return 0;
}

从中公开继承是一个派生类,它不应该转换为 Base。一点也不。

从OOP设计的角度来看,这两件事几乎是矛盾的。我认为语言中没有办法防止将派生对象视为公共基础(通过引用的隐式转换)。

您可以非公开地继承Base

这也将防止显式转换 - 您仍然希望能够这样做。但仅限于类的范围之外。您可以改为提供一个成员来访问基本实例:

class Derived : Base
{
public:
    // ...
    Base& base() {
        return *this;
    }
};

现在,您可以将这些显式强制转换替换为对 Derived::base 的调用,这仍然是允许的,而隐式转换则不允许。

Derived d;
Base& b = d;        // (implicit) conversion fails
Base& b = d.base(); // this works

您可能还希望实现该函数的const版本。我将把这作为一项练习。

我的标准方法是使用私有继承,然后用 using 解除所有想要的方法、构造函数和运算符。

class Derived : private Base {
  public :
  using Base::Base;
  using Base::some_base_method;
}
int main(){
  Derived d1(2);
  d1.some_base_method();
}

无论糟糕还是更糟,这也使得许多未来通过 Derived 的公共接口无法添加到 Base 中。它也适用于包括 CRTP 在内的模板。

由于您没有继承替换(您明确表示不希望将派生的外观用作基础),因此我假设您继承了一个实现。在这种情况下,答案很明确:根据您的确切需求继承private或可能protected。这完全禁止所有此类转换为基类,同时仍允许你的孩子访问基类的实现。

如果需要公开一个或两个基成员,则始终可以将它们using派生类中。