Почему функции члена класса теневые функции с тем же именем?

Недавно мне стало известно, что функции-члены полностью скрывают свободные функции с тем же именем внутри класса. Под "полным" я подразумеваю, что каждая свободная функция с тем же именем вообще не рассматривается для разрешения перегрузки. Я могу понять, почему это произошло с чем-то вроде этого:

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
    }
};

Я не спрашиваю, как вызвать скрытую свободную функцию внутри класса. То, что я хочу знать, является обоснованием этого дизайна.

Ответ 1

Для поиска неквалифицированного имени рассматривается только одна область за раз, и если поиск в этой области не дает никаких результатов, выполняется поиск следующей более высокой области. В вашем случае выполняется поиск только области S.

Но зачем запрещать однозначные звонки, где свободная функция имеет различную подпись:

Проблема заключается в том, что поиск имени не касается ничего, кроме имени, идентификатора. Это совершенно не обращает внимания на то, что вы хотите вызвать функцию, она просто видит идентификатор. Тот же поиск имен происходит, если вы просто используете auto x = f;, и если вы так думаете об этом, есть очень веские причины, по которым вам требуется очень ограниченная область поиска. Все остальное просто удивит пользователя.

Ответ 2

Существует специальное, очень удивительное правило (но это не относится к вашему примеру), заявляя, что после поиска имени класса по поиску по имени не просматриваются области пространства имен:

#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)
}

ИМО, это сумасшествие.

Но зачем запрещать однозначные звонки, где свободная функция имеет различную подпись:

Поиск имени происходит до перегрузки разрешения:

  • Если поиск неоднозначен, перегрузка разрешения не выполняется.
  • Если никакая жизнеспособная функция не найдена при поиске по имени, никакой другой раунд поиска не проверяется.

Правила достаточно сложны без "обратной связи" между перегрузкой и поиском имен. Я бы предложил упрощение (например, удаление правила списка имен пространства имен для скрытых имен элементов и удаление неоднозначного поиска имен), а не комплексификации.

Ответ 3

Я не могу дать авторитетный ответ (возможно, некоторые помнят цитату из Design and Evolution of C++ или фактически были в комитете в то время), но моя первая догадка заключалась бы в том, чтобы точно потерпеть неудачу в случаях, когда вы показываете. Легко забыть, сколько вещей доступно в определенный момент времени. Кроме того, разрешение перегрузки может быть довольно сложным, и могут быть аргументы и преобразование по умолчанию. Поэтому я бы предпочел, чтобы в этом случае была максимально ограниченная область, чтобы всегда быть уверенным, что именно вызывается.