Является ли 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/лямбда на g?


person YSC    schedule 12.07.2018    source источник
comment
Обратите внимание, что если вы объявляете (но не определяете) f до или после определения S, все в порядке.   -  person Martin Bonner supports Monica    schedule 12.07.2018


Ответы (3)


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

Да. Как указано в [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).

person StoryTeller - Unslander Monica    schedule 12.07.2018
comment
первая декларация меня заинтриговала. Означает ли это, что для того, чтобы f был видимым, он должен быть объявлен до его определения в S? Если да: coliru.stacked-crooked.com/a/a10f693434919727 - person YSC; 12.07.2018
comment
@YSC - его также можно объявить впоследствии. Смысл здесь в том, что если это единственное объявление, доступное до определенного момента, оно не отображается до этого момента. - person StoryTeller - Unslander Monica; 12.07.2018

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

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

person songyuanyao    schedule 12.07.2018
comment
если не предоставлено соответствующее объявление в области пространства имен, я бы ответил Нет на вопрос. (или, по крайней мере, это зависит) - person YSC; 12.07.2018
comment
@MartinBonner - Когда-то существовала такая штука, как инъекция имени друга. Его убрали ближе к первой стандартной публикации. Вот документ с включенным обоснованием open-std .org/jtc1/sc22/wg21/docs/papers/1995/N0777.pdf - person StoryTeller - Unslander Monica; 12.07.2018

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

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

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

person user7860670    schedule 12.07.2018