25

Let us define f, as a friend function of S, inside the declaration of S:

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

I cannot find a way to call f.

Is it true, then, that such an inline friend function can only be called with argument-dependant lookup?

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

Bonus: what if I want to get a function-pointer/std::function/lambda to g?

curiousguy
  • 8,038
  • 2
  • 40
  • 58
YSC
  • 38,212
  • 9
  • 96
  • 149

3 Answers3

12

Is it true, then, that such an inline friend function can only be called with argument-dependant lookup?

Yes. As specified in [namespace.memdef]/3:

If a friend declaration in a non-local class first declares a class, function, class template or function template. the friend is a member of the innermost enclosing namespace. The friend declaration does not by itself make the name visible to unqualified lookup ([basic.lookup.unqual]) or qualified lookup ([basic.lookup.qual]).

Since the only declaration of f is its inline definition, it's not made visible to qualified or unqualified lookup. ADL however, has a special provision for such friend functions, [basic.lookup.argdep]/4:

When considering an associated namespace, the lookup is the same as the lookup performed when the associated namespace is used as a qualifier ([namespace.qual]) except that:

  • Any namespace-scope friend functions or friend function templates declared in associated classes are visible within their respective namespaces even if they are not visible during an ordinary lookup ([class.friend]).

As for your bonus question, a lambda should do it:

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

It wraps the ADL into its body. Though the usual caveats about return type deduction apply. It will be a value (assuming you don't return void).

StoryTeller - Unslander Monica
  • 165,132
  • 21
  • 377
  • 458
  • 1
    the _"**first** declares"_ intrigues me. Does it mean for `f` to be visible it must be declared _before_ it is define in `S`? If yes: http://coliru.stacked-crooked.com/a/a10f693434919727 – YSC Jul 12 '18 at 11:44
  • 4
    @YSC - It can also be declared afterwards. The meaning here that if it's the only declaration available up to a point, it isn't visible up to that point. – StoryTeller - Unslander Monica Jul 12 '18 at 11:45
10

The name f is declared in a friend declaration, even it becomes the member of the namespace which contains S, but it's not visible for name lookup, unless it's redeclared at namespace scope. If not, it could be found only by ADL.

Names introduced by friend declarations within a non-local class X become members of the innermost enclosing namespace of X, but they do not become visible to ordinary name lookup (neither unqualified nor qualified) unless a matching declaration is provided at namespace scope, either before or after the class definition. Such name may be found through ADL which considers both namespaces and classes.

Oliv
  • 17,610
  • 1
  • 29
  • 72
songyuanyao
  • 169,198
  • 16
  • 310
  • 405
  • _"unless a matching declaration at the namespace scope is provided"_ would make me answer "No." to the question. (or at least, "It depends") – YSC Jul 12 '18 at 11:38
  • 2
    @MartinBonner - There once was a thing called "friend name injection". It was removed towards the first standard publication. Here's the paper, with rationale included http://www.open-std.org/jtc1/sc22/wg21/docs/papers/1995/N0777.pdf – StoryTeller - Unslander Monica Jul 12 '18 at 11:59
1

No. You should just declare function properly.

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

online compiler

user7860670
  • 35,849
  • 4
  • 58
  • 84