Why is a public const method not called when the non-const one is private?
考虑此代码:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 | struct A { void foo() const { std::cout <<"const" << std::endl; } private: void foo() { std::cout <<"non - const" << std::endl; } }; int main() { A a; a.foo(); } |
编译器错误为:
error: 'void A::foo()' is private`.
但是当我删除了这个私有的,它就工作了。当非常量方法是私有的时,为什么不调用公共常量方法?
换句话说,为什么超负荷解决方案比访问控制来得早?这很奇怪。你认为这是一致的吗?我的代码可以工作,然后我添加了一个方法,我的工作代码根本不编译。
当您调用
1 | void foo() const |
和
1 | void foo() |
现在,由于
记住,在过载分辨率中,它不是"找到最佳可用函数"。它是"找到最好的功能并尝试使用它"。如果由于访问限制或被删除而无法执行,则会出现编译器错误。
In other words why does overload resolution comes before access control?
好吧,让我们看看:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 | struct Base { void foo() { std::cout <<"Base "; } }; struct Derived : Base { void foo() { std::cout <<"Derived "; } }; struct Foo { void foo(Base * b) { b->foo(); } private: void foo(Derived * d) { d->foo(); } }; int main() { Derived d; Foo f; f.foo(&d); } |
现在让我们说,我并不是有意让
最终,这归结于标准中的断言,即在执行过载解决方案时,不应考虑可访问性。此断言可在[over.match]第3条中找到:
... When overload resolution succeeds, and the best viable function is not accessible (Clause [class.access]) in the context in which it is used, the program is ill-formed.
以及同一节第1条中的注释:
[ Note: The function selected by overload resolution is not guaranteed to be appropriate for the context. Other restrictions, such as the accessibility of the function, can make its use in the calling context ill-formed. — end note ]
至于原因,我可以考虑几个可能的动机:
假设访问控制早于过载解决方案。实际上,这意味着
Stroustrup的C++设计和演化的第2.10节在这里讨论了下面的例子
1 2 3 4 5 6 7 8 9 10 | int a; // global a class X { private: int a; // member X::a }; class XX : public X { void f() { a = 1; } // which a? }; |
stroustrup提到,当前规则(在可访问性之前的可见性)的一个好处是(暂时)将
他接着说,他不记得它是否是通过显式设计或副作用的预处理器技术用于实现C与前级标准C + +。
这与您的示例有什么关系?基本上是因为标准使重载解决方案符合一般规则,即名称查找先于访问控制。
10.2 Member name lookup [class.member.lookup]
1 Member name lookup determines the meaning of a name (id-expression)
in a class scope (3.3.7). Name lookup can result in an ambiguity, in
which case the program is ill-formed. For an id-expression, name
lookup begins in the class scope of this; for a qualified-id, name
lookup begins in the scope of the nestedname- specifier. Name lookup
takes place before access control (3.4, Clause 11).8 If the name of an overloaded function is unambiguously found,
overloading resolution (13.3) also takes place before access control.
Ambiguities can often be resolved by qualifying a name with its class
name.
由于隐式
如果显式标记非
重要的是要记住事情发生的顺序,即:
(3)发生在(2)之后。这一点非常重要,因为否则使函数
在这种情况下:
这归结为C++中一个相当基本的设计决策。
当查找函数以满足调用时,编译器执行如下搜索:
它会搜索第一个作用域,在这个作用域中有一个同名的东西。
编译器在该范围内查找具有该名称的所有函数(或函数等)。
然后编译器进行重载解析,以在找到的候选项中找到最佳候选项(无论它们是否可访问)。
最后,编译器检查所选函数是否可访问。
由于这个顺序,是的,编译器可能会选择一个不可访问的重载,即使有另一个可访问的重载(但在重载解析期间没有选择)。
至于是否可以做不同的事情:是的,这无疑是可能的。它肯定会导致与C++完全不同的语言。事实证明,许多看似非常微小的决定可能会产生比最初明显的影响更大的后果。
访问控制(
在这个电话里:
1 | a.foo(); |
在每个成员函数中总是有一个隐式
1 | A::foo(a); |
但你有两份关于
1 2 | A::foo(A* ); A::foo(A const* ); |
通过过载决议,第一个将被选为非常数dOCx1〔3〕,第二个将被选为非常数dOCx1〔14〕。如果删除第一个,第二个将绑定到
过载解决后,选择最佳可行的功能,来进行访问控制。由于您将对所选重载的访问指定为
标准规定:
[class.access/4]: ...In the case of overloaded function names, access control is applied to
the function selected by overload resolution....
但如果你这样做:
1 2 3 | A a; const A& ac = a; ac.foo(); |
然后,只适合
技术原因已由其他答案回答。我只关注这个问题:
In other words why overload resolution comes before access control? This is strange. Do you think it is consistent? My code works and then I add a method and my working code does not compile at all.
语言就是这样设计的。其目的是尽可能称之为最佳可行过载。如果失败,将触发一个错误,提醒您重新考虑设计。
另一方面,假设您的代码经过编译,并与被调用的
访问说明符永远不会影响名称查找和函数调用解析。在编译器检查调用是否应触发访问冲突之前,将选择该函数。
这样,如果更改访问说明符,则在编译时,如果现有代码中存在冲突,您将收到警报;如果在函数调用解析时考虑到隐私,则程序的行为可能会自动更改。
因为
对常量对象调用常量成员函数。