当 const 方法是公共的并且非 const 方法受到保护时,为什么C++不强制转换为 const?

Why doesn't C++ cast to const when a const method is public and the non-const one is protected?

本文关键字:const 方法 C++ 为什么 转换 保护      更新时间:2023-10-16

我创建了一个具有两个get方法的类,一个是const方法,一个是非const方法。const方法是公共的,因此用户可以查询vector。非const方法是受保护的,所以我可以用它来修改我需要的数据。

但是,当我尝试使用类并调用get方法时,编译器报错非const方法受到保护。相反,我必须使用const_cast将对象强制转换为const,这样我就可以获得公共方法。

有办法解决这个问题吗?既然有一个公共方法,为什么编译器不自己进行强制类型转换呢?如果我删除受保护的版本,只留下const版本,它会很好地工作,在这种情况下它会进行强制转换。强制转换为const总是安全的。

成员访问控制是调用成员函数时发生的最后一件事。它发生在名称查找、模板参数推导、重载解析等之后。最后做的原因是,更改成员的访问控制不应该突然改变客户端代码的执行。

假设在重载解析之前检查了访问,并且使用了一个库和该库中的某个成员函数。然后库作者将该函数设为私有。突然之间,您的代码开始使用不同的重载并以完全不同的方式运行。库作者可能想让任何使用函数重载的人停止使用它,但他们不打算改变每个人的代码。然而,随着标准的实际定义,您的代码现在会因为使用私有成员而开始给您一个错误,而不是表现不同。

解决方案是简单地更改受保护的成员函数的名称,使其不被考虑。

编译器在之后考虑可访问性,它决定要调用哪个成员函数。也就是说,受保护函数和私有函数仍然可见,即使它们不能访问

为什么?一个原因是,如果您让不可访问的函数被重载解析忽略,那么您可以简单地通过更改其可访问性来更改调用的函数。使用当前的规则,您只能导致编译代码无法编译,或者导致当前无法编译的代码编译,或者更改某些内容而不会对代码的含义产生影响。您不能更改访问说明符而静默地导致调用不同的函数。

作为一个人为的例子,下面是一个非常糟糕的类接口:

public:
    // Returns the amount of change tendered for this transaction.
    MoneyAmount change() const;
private:
    // Account for a change of currency. Charges standard moneychanger's fee.
    MoneyAmount change(Currency toCurrency = Currency::USD);

如果从重载解析中删除不可访问的函数,客户端代码可以很好地调用change()。如果后来第二个change(Currency)函数被公开,而第一个被删除,那么该代码将突然以完全不同的目的静默地调用另一个函数。当前规则防止访问说明符的更改改变编译程序的行为。

在c++中,方法选择(重载解析)发生在考虑公共/私有访问控制之前。

使用受保护的setter方法(或数据成员)代替非const的getter方法。

如果你有s。像

class A {
    SomeType foo_;
protected:
    SomeType& foo() { return foo_; }
public:
    const SomeType& foo() const { return foo_; }
};

class A {
protected:
    SomeType foo_;
public:
    const SomeType& foo() const { return foo_; }
};

这是c++的自然行为,如果调用者代码中的类的对象是非const的,则不进行转换,即定义为受保护的对象。您需要将类的对象定义为const,或者对类的对象使用const强制转换,这将导致调用方法的const版本。

#include <iostream>
class Foo {
    public:
        const void bar() const { std::cout << " const version calledn";}  
    protected:
        void bar() { std::cout << " non const version calledn";}  
};
int main(int argc, char** argv)
{
    const Foo c;
    c.bar(); // -> work
    Foo c1;
    c1.bar(); // compiler complain -> void Foo::bar() is protected
    const_cast<const Foo&>(c1).bar(); // fine 
}

有一条关于访问控制的铁律:更改访问控制只能使以前没有编译的代码编译,或者使以前编译过的代码停止编译。它永远,永远不能改变之前和之后编译的代码的含义。

如果不是这条规则,你会有太多糟糕的惊喜。