Почему вызов функции, не являющейся членом, с тем же именем, что и функция-член, генерирует ошибку

У меня есть следующий код:

void f(int){}

struct A
{
    void f()
    {
        f(1);
    }
};

Этот код плохо сформирован с сообщением об ошибке (GCC): error: no matching function for call to ‘A::f(int)’ или (clang) Too many arguments to function call, expected 0, have 1; did you mean '::f'?

Зачем мне нужно использовать :: для вызова функции, не являющейся членом, с тем же именем, что и функция-член, но с другой подписью? Какова мотивация этого требования?

Я думаю, что компилятор должен уметь это понять. Я хочу вызвать функцию нечлена, поскольку подпись отличается (clang даже помещает это в сообщение об ошибке!).

Пожалуйста, не помещайте это как дубликат - это другой вопрос из этого Вызов в С++ не член-функции внутри класса с методом с тем же

Ответ 1

Зачем мне нужно использовать:: для вызова функции non-member с тем же именем, что и функция-член, но с другой подписью

Потому что это правила. Имена во вложенной области скрывают объекты с тем же именем в более широкой области.

Какова мотивация этого требования?

Рассмотрим случай, когда функция-член вызывает другой элемент с неподходящей сигнатурой:

struct A {
    void f(double);
    void g() {f(42);}  // requires int->double conversion
};

Теперь предположим, что кто-то добавляет несвязанную функцию в окружающее пространство имен

void f(int);

Если бы это было включено в набор перегрузок в пределах области A, тогда внезапно изменилось поведение A::g: он назвал бы это вместо A::f. Ограничение перегрузки, установленное на имена в самой узкой доступной области, предотвращает такой неожиданный поломка.

Как вы (и ваш полезный компилятор) говорите, что внешнее имя по-прежнему доступно (с квалификацией), если вам это нужно.

Ответ 2

Компилятор выполняет поиск неквалифицированного имени, для f, указанного в §3.4.1 [basic.lookup.unqual] (к счастью, здесь нет ADL):

1 Во всех случаях, перечисленных в 3.4.1, поиск областей осуществляется объявление в порядке, указанном в каждой из соответствующих категорий; поиск имени заканчивается, как только будет найдено объявление для имени. Если нет объявление найдено, программа плохо сформирована.

8 Для членов класса X имя, используемое в функции-члене body, в аргументе по умолчанию, в спецификации исключения, в (9.2) или в определении члена класса, не входящего в определение X, после декларатора-идентификатора участников, объявляется в одном из следующие способы:

  • перед его использованием в блоке, в котором он используется, или в закрывающем блоке (6.3), или
  • должен быть членом класса X или быть членом базового класса X (10.2) или
  • если X является вложенным классом класса Y (9.7), должен быть членом Y или должен быть членом базового класса Y (применяется этот поиск в свою очередь, к классам Y s, начиная с самого внутреннего охватывающий класс), или
  • если X является локальным классом (9.8) или является вложенным классом локального класса, перед определением класса X в блоке, охватывающем определение класса X или
  • if X является членом пространства имен N или является вложенным классом класса, который является членом N, или является локальным классом или вложенным классом внутри локальный класс функции, являющейся членом N, до использования имя, в пространстве имен N или в одном из охватываемых пространств имен N.

Поиск имен прекращается, как только объявляется объявление. Поэтому, когда он находит элемент f() во второй точке, он останавливается и никогда не ищет в другом месте.

Отклонение нежизнеспособных функций выполняется после поиска имени при разрешении перегрузки.

Ответ 3

Зачем мне нужно использовать:: для вызова функции non-member с тем же именем, что и функция-член, но с другой подписью? Какова мотивация этого требования?

В этом весь смысл пространства имен. Локальное (более узкое) имя является предпочтительным и более видимым над глобальным именем. Поскольку a struct снова является областью действия, его f затеняет видимость ::f. Когда вы должны иметь глобальный, вы должны сказать, что делаете. Почему?

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

Ответ 4

Чтобы понять причину вашей ошибки и почему вам нужно явно использовать синтаксис ::f(), вы можете рассмотреть некоторые аспекты процесса компилятора С++:

Первое, что делает компилятор, это поиск имени.

Неквалифицированный поиск имени начинается с текущей области, а затем перемещается наружу; он останавливается, как только он находит объявление для имени функции, даже если эта функция будет позже определена, чтобы не быть жизнеспособным кандидатом для вызова функции.

Затем выполняется разрешение перегрузки по набору функций поиска имен.

(И, наконец, проверка доступа выполняется над функцией, которая получила разрешение перегрузки.)