Вопрос о поиске имени с функцией друга - PullRequest
4 голосов
/ 23 апреля 2020

Я прочитал стандартный раздел [basi c .lookup.unqual], и меня это смущает:

typedef int f;
namespace N {
  struct A {
    friend void f(A &);
    operator int();
    void g(A a) {
      int i = f(a);  // f is the typedef, not the friend function: equivalent to int(a)
    }
  };
}

Пожалуйста, рассмотрите приведенный выше код; Я не понимаю, почему имя f имеет тип int, а не void f(A &). В моем понимании поиск имени должен сначала найти void f(A &) в области класса A. Если имя там не может быть найдено, он выполнил бы поиск во внешнем пространстве имен. Очевидно, что в классе A есть имя void f(A &) и, как гласит стандарт:

поиск имени заканчивается, как только найдено объявление для имени

Так почему же имя относится к типу int здесь, если есть другие конкретные правила по этому поводу?

1 Ответ

5 голосов
/ 23 апреля 2020

Во-первых, само объявление само по себе не делает f видимым для поиска по имени, f может быть найдено только ADL.

Имя, впервые объявленное в объявлении друга в пределах класса или шаблона класса X становится членом внутреннего вложенного пространства имен X, но не отображается для поиска (кроме поиска, зависящего от аргумента, который учитывает X), если не предоставлено соответствующее объявление в области пространства имен

Из стандарта [namespace.memdef] / 3 ,

Объявление друга само по себе не делает имя видимым для неквалифицированного или квалифицированного поиска , [Примечание: имя друга будет отображаться в его пространстве имен, если в области имен пространства предоставляется соответствующее объявление (либо до, либо после определения класса, предоставляющего дружбу). - примечание конца] Если вызывается функция или шаблон функции друга, ее имя может быть найдено с помощью поиска имен, который рассматривает функции из пространств имен и классов, связанных с типами аргументов функции ([basi c .lookup.argdep]) , Если имя в объявлении друга не является ни квалифицированным, ни идентификатором шаблона, и объявление является функцией или разработанным спецификатором типа, поиск, чтобы определить, был ли объект ранее объявлен, не должен рассматривать какие-либо области за пределами самого внутреннего окружающего пространства имен .

Проблема заключается в том, что для применения ADL необходимо заранее определить, является ли f(a) вызовом функции.

[basi c .lookup. unqual] / 3 ,

(выделено мной)

Поиск неквалифицированного имени, используемого в качестве постфиксного выражения для вызова функции, описан в [basi c .lookup.argdep]. [Примечание: В целях определения (во время синтаксического анализа), является ли выражение постфиксным выражением для вызова функции, применяются обычные правила поиска имен .

На этом этапе имя функции f невидимо и найдено имя типа f, тогда f(a) считается не функцией всех, тогда ADL вообще не будет применяться.

Поскольку выражение не является вызовом функции, поиск имени в зависимости от аргумента ([basi c .lookup.argdep]) не применяется, и функция f друга не найдена.

КСТАТИ: Добавление объявление f в области имен делает имя функции f видимым, а f(a) будет рассматриваться как вызов функции (и тогда вы получите ошибку, что f возвращает void, которая не может быть использована инициализировать i). например,

typedef int f;
namespace N {
  struct A;
  void f(A &);
  struct A {
    friend void f(A &);
    operator int();
    void g(A a) {
      int i = f(a);  // f is the friend function now
    }
  };
}
...