为什么类成员函数使用相同名称阴影自由函数

Why class member functions shadow free functions with same name?

本文关键字:函数 阴影 自由 成员 为什么      更新时间:2023-10-16

最近我注意到,成员函数在类内完全shadow释放同名函数。我所说的完全是指,所有具有相同名称的自由函数根本不考虑重载解析。我能理解为什么要用这样的东西:

void f();
struct S
{
    void f();
    void g()
    {
        f(); // calls S::f instead of ::f
    }
};

在函数具有相同签名的情况下,其唯一的自然变量作用域以相同的方式工作。但是为什么要禁止自由函数有不同签名的无野心调用呢

void f();
struct S
{
    void f(int x);
    void g()
    {
        f(); // fails to compile attempting to call S::f, which has wrong signature
    }
};

我不是在问如何从类内部调用阴影自由函数。我想知道的是这个设计背后的原理。

对于非限定名称查找,一次只考虑一个作用域,如果在该作用域中的搜索没有产生任何结果,则搜索下一个更高的作用域。在您的情况下,只搜索S的作用域。

但是为什么要禁止自由函数有不同签名的无野心调用呢

问题是名称查找除了名称、标识符之外,什么都不关心。它完全忽略了这样一个事实:你想调用一个函数,它只看到一个标识符。如果你只使用auto x = f;,就会发生相同的名称查找,如果你这样想的话,有很好的理由你只想要一个非常有限的范围来搜索。其他任何事情都会让用户大吃一惊。

有一个特殊的、非常令人惊讶的规则(但它不适用于您的示例)指出,一旦通过名称查找找到类成员名称,就不会搜索任何命名空间范围:

#include <string>
struct C {
    std::string s;
    explicit C (std::string);
    void swap (C& rhs) {
        swap (s, rhs.s); // error: swap is C::swap
    }   
};
void swap (C& lhs, C& rhs) {
    swap (lhs.s, rhs.s); // std::swap(string,string)
}

IMO,这太疯狂了。

但是为什么要禁止自由函数有不同签名的无野心调用呢

名称查找发生在重载解析之前:

  • 如果查找不明确,则不会执行重载解析
  • 如果通过名称查找找不到可行的函数,则不会尝试其他一轮查找

规则足够复杂,重载和名称查找之间没有"反馈"。我建议简化(比如删除成员隐藏名称空间范围名称规则,以及删除不明确的名称查找),而不是复杂化。

我无法提供权威的答案(也许有些人记得Design and Evolution of C++的一句话,或者当时确实是委员会的成员),但我的第一个猜测是,正如你所展示的那样,在某些情况下会完全失败。人们很容易忘记在某个时间有多少东西在范围内。此外,重载解析可能相当复杂,并且可能存在默认参数和转换。因此,在这种情况下,我宁愿拥有最有限的范围,以始终确定确切的名称。