Является ли ADL единственным способом вызова встроенной функции друга?

Определим f, как функцию друга S, внутри объявления S:

struct S
{
    friend void f() {}
};

Я не могу найти способ вызвать f.

Верно ли, что такая функция встроенного друга может быть вызвана только с зависимым от аргумента поиска?

struct S
{
    friend void f() {}
    friend void g(S const&) {}
} const s;

int main()
{
    // f();     // error: 'f' was not declared in this scope
    // S::f();  // error: 'f' is not a member of 'S'
    g(s);
    // S::g(s); // error: 'g' is not a member of 'S'
}

Бонус: что, если я хочу получить функцию-указатель/std::function/lambda to g?

Ответ 1

Верно ли, что такая функция встроенного друга может быть вызвана только с зависимым от аргумента поиска?

Да. Как указано в [namespace.memdef]/3:

Если объявление friend в нелокальном классе сначала объявляет класс, функцию, шаблон шаблона или шаблон функции. друг является членом самого внутреннего охватывающего пространства имен. Объявление друга само по себе не делает имя видимым для неквалифицированного поиска ([basic.lookup.unqual]) или квалифицированного поиска ([basic.lookup.qual]).

Поскольку единственным объявлением f является его встроенное определение, оно не становится видимым для квалифицированного или неквалифицированного поиска. Однако ADL имеет специальное положение для таких функций друга, [basic.lookup.argdep]/4:

При рассмотрении связанного пространства имен поиск выполняется так же, как поиск выполняется, когда соответствующее пространство имен используется в качестве классификатора ([namespace.qual]), за исключением того, что:

  • Любые функции имени пространства имен или шаблоны функций друзей, объявленные в связанных классах, видны в пределах их соответствующих пространств имен, даже если они не видны во время обычного поиска ([class.friend]).

Что касается вашего бонусного вопроса, лямбда должна это сделать:

auto exposed_g = [](S const& s){ g(s); };

Он обертывает ADL в его тело. Хотя обычные оговорки о вычете типа возврата применяются. Это будет значение (если вы не вернете void).

Ответ 2

Имя f объявляется в объявлении друга, даже оно становится членом пространства имен, которое содержит S, но оно не отображается для поиска по имени, если только оно не переопределено в области пространства имен. Если нет, это может быть найдено только ADL.

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

Ответ 3

Нет. Вы должны просто объявить функцию должным образом.

struct S;
inline void f();
inline void g(S const&);

struct S
{
    friend void f() {}
    friend void g(S const&) {}
} const s;

int main()
{
    f(); // Ok
    // S::f();  // error: 'f' is not a member of 'S'
    g(s);
    // S::g(s); // error: 'g' is not a member of 'S'
}

онлайн-компилятор