37

After searching aroung SO, one question taught me that the lexical scope of an inline friend function is the class it's defined in, meaning it can access e.g. the typedefs in the class without qualifying them. But then I wondered what is the actual scope of such a function? GCC at least rejects all my attempts to call it. Can a function such as in the example ever be called through means other than ADL, which is not possible here thanks to no arguments?

Standard quotations are appreciated, as I currently can't access my copy of it.

The following code

namespace foo{
  struct bar{
    friend void baz(){}
    void call_friend();
  };
}

int main(){
  foo::baz();           // can't access through enclosing scope of the class
  foo::bar::baz();    // can't access through class scope
}

namespace foo{
  void bar::call_friend(){
    baz();    // can't access through member function
  }
}

results in these errors:

prog.cpp: In function ‘int main()’:
prog.cpp:9: error: ‘baz’ is not a member of ‘foo’
prog.cpp:10: error: ‘baz’ is not a member of ‘foo::bar’
prog.cpp: In member function ‘void foo::bar::call_friend()’:
prog.cpp:15: error: ‘baz’ was not declared in this scope
Andrew Marshall
  • 95,083
  • 20
  • 220
  • 214
Xeo
  • 129,499
  • 52
  • 291
  • 397

6 Answers6

41

When you declare a friend function with an unqualified id in a class it names a function in the nearest enclosing namespace scope.

If that function hasn't previously been declared then the friend declaration doesn't make that function visible in that scope for normal lookup. It does make the declared function visible to argument-dependent lookup.

This is emphasised in many notes, but the definitive statement is in 7.3.1.2/3 (of ISO/IEC 14882:2011):

Every name first declared in a namespace is a member of that namespace. If a friend declaration in a non-local class first declares a class or function the friend class or function is a member of the innermost enclosing namespace. The name of the friend is not found by unqualified lookup (3.4.1) or by qualified lookup (3.4.3) until a matching declaration is provided in that namespace scope (either before or after the class definition granting friendship). If a friend function is called, its name may be found by the name lookup that considers functions from namespaces and classes associated with the types of the function arguments (3.4.2). If the name in a friend declaration is neither qualified nor a template-id and the declaration is a function or an elaborated-type-specifier, the lookup to determine whether the entity has been previously declared shall not consider any scopes outside the innermost enclosing namespace.

CB Bailey
  • 755,051
  • 104
  • 632
  • 656
  • 2
    Perfect, it's even the same paragraph in the C++03 standard. Quite hidden that statement... Thanks! – Xeo Nov 27 '11 at 16:53
6

"The C++ Programming Language 3rd Edition (Stroustrap)" : p279:

I. "Like a member declaration, a friend declaration does not introduce a name into an enclosing scope"

II. "A friend class must be previously declared in an enclosing scope or defined in the nonclass scope immediately enclosing the class that is declaring it a friend"

III. "A friend function can be explicitly declared just like friend classes, or it can be found through its argument types (§8.2.6) as if it was declared in the nonclass scope immediately enclosing its class."

IV. "It follows that a friend function should either be explicitly declared in an enclosing scope or take an argument of its class. If not, the friend cannot be called. For example:"

//no f() here
void g();
class X{
    friend void f();          //useless
    friend void g();          //can be found because it is declared outside of class scope
    friend void h(const X&);  //can be found because the arguments access class members
};

void f() { }                 //enemy of X :)

But in your case there is more to it that has to do with the namespace, because if you put the proper declaration in foo e.g.:

namespace foo{
  struct bar{
    friend void baz(const &bar){};
    void call_friend();
  }
}

does not compile. However, if you declare it outside foo is works like a charm. Now consider that in fact, global, local, struct, and classes are in fact namespaces. Now this leads to the conclusion that the baz(const &) is implicitly defined in the global scope.

This compiles:

namespace foo{
  struct bar{
    friend void baz(const bar&){};
    void call_friend();
  };
}

int main(){
    foo::bar k;
    baz(k);
    return 0;
}

Therefore, there are two issues:

  1. The friend declaration does not introduce a name in an enclosing scope, unless IV. Thus the original program cannot find baz() because it has not been properly declared.
  2. If IV , i.e. ADL, then the function is found in foo, but cannot be accessed as foo::baz(k), due to ADL. You will have to explicitely define baz(const bar&) in foo to access it by qualified name.

Thanks, hope it helps, but certainly, I liked the challenge :) .

  • `it is found in the global scope` - not really, it is just not callable through a qualified lookup in the namespace `foo`. That does not mean it is in the global scope. Also, the standard quote @Charles posted seems to contradict that `f` is useless: `(either before or **after** the class definition granting friendship)`, which is what you did here (or is the `void f(){}` after `X` also in the book? I believe not). – Xeo Nov 29 '11 at 09:15
  • it is after X . You should take a look at the book. I cite the page. The snippet is copied from there. –  Nov 29 '11 at 10:36
  • @Xeo: I edited to include a few more lines from the book, and add the definition of call_friend() code. –  Nov 29 '11 at 10:54
  • Interesting. But that would mean Bjarne contradicts the standard in that snippet, because the standard explicitly allows the definitions of friend functions after the class definition. – Xeo Nov 29 '11 at 12:15
  • I have to admit though that baz(k) is not in the global scope since ::baz(k) will not compile with error : "error: the global scope has no "baz"". However, it seem that it is found through an unqualified lookup, which is indeed strange, because foo::baz(k) still fails. I would expect according to definition to be in foo, due to III. Hence point 2 is not true. I am looking into it. :\ –  Nov 29 '11 at 12:33
  • Look at the standard quote at the top. :P `The name of the friend is not found by unqualified lookup (3.4.1) or by qualified lookup (3.4.3) until a matching declaration is provided in that namespace scope`, meaning it can only be found through ADL lookup (which won't happen without proper parameters). – Xeo Nov 29 '11 at 12:47
  • Yes, but Xeo , you declare baz() without arguments. Though it cannot be found through ADL, and I declare it implicitly with arguments, thus is can be found through ADL only. So in your case, there is no declaration whatsoever. In my case according to Bjarne, it is in foo, but cannot be found by qualified lookup but only through ADL. –  Nov 29 '11 at 13:32
  • I will align my answer above to that , and that's as far as I can go. :\ hope you find your answer, if mine is not sufficient. :) –  Nov 29 '11 at 13:33
  • Th example in section 11.5.1 of the book, which has `f` defined in global scope as: `void f(){} // not a friend of X`, whose comment you changed to `enemy of X` is wrong, as the function `f` is really a friend of the class `X`, as you can see in [this example](http://coliru.stacked-crooked.com/a/d356be00c170792e). – Alexander Aug 14 '18 at 14:26
2

Interesting!

It seems that the compiler does not know what scope it belongs to (and to be honest there are no clues) and thus puts in in no scope. Some standard digging coming up I suppose.

Note: If you explicitly add a declaration to a particular scope then it starts to work as expected.

namespace foo
{
  void baz();   // declare it here and now it works in foo namespace etc.
  struct bar
  {
    friend void baz(){}
    void call_friend();
  };
}

Digging the standard I find:

11.3 Friends [class.friend]

Paragraph 6

A function can be defined in a friend declaration of a class if and only if the class is a non-local class (9.8), the function name is unqualified, and the function has namespace scope.

[ Example:
class M { friend void f() { }       // definition of global f, a friend of M,
                                    // not the definition of a member function
};
— end example ]

Paragraph 7

Such a function is implicitly inline. A friend function defined in a class is in the (lexical) scope of the class in which it is defined. A friend function defined outside the class is not (3.4.1).

Note:

A free standing function that does not take a parameter is not much use as a friend. As it will have no object on which to take advantage of its friendship (I suppose file scope static storage duration objects).

Matthieu M.
  • 287,565
  • 48
  • 449
  • 722
Martin York
  • 257,169
  • 86
  • 333
  • 562
  • I am wondering about the "function has namespace scope" bit. Does it mean that the resulting function now has namespace scope or that the function being defined inline as friend should have had namespace scope prior to the definition ? – Matthieu M. Nov 21 '11 at 07:39
  • @MatthieuM.: The way I read that is that the function belongs to a namespace and not the class (though it seems unspecified which namespace). – Martin York Nov 21 '11 at 18:56
  • @Matthieu: I'm reading it the latter way too, since it seems to be part of the "if and only if" clause. – Xeo Nov 22 '11 at 03:50
  • @MartinYork I'm defining the `friend` function inside the local class and I can still access it inside `main()`. That conflicts with what you wrote in the blockquote under **Paragraph 6**. – Shubham Jan 26 '21 at 08:45
  • @Lucas That is as expected. The function is visible in the same namespace as the class. If this is in the global namespace then it should be visable without qualification in main(). This as expected. – Martin York Jan 26 '21 at 19:10
1

A friend function defined within an enclosing class, will only be found by argument dependent lookup (ADL). Calling it will succeed when one or more of the arguments is either of the enclosing class type; or of a type declared within the class. Here is an example, displaying "Hello World!", which (for variety) doesn't use an object of the enclosing class type to provide such an argument:

#include <iostream>

struct foo
{
  struct local_class{};
  friend void greet(local_class o, int) { std::cout << "Hello World!\n"; }
};

int main(int argc, char *argv[])
{
  foo::local_class o;
  greet(o,1);

  return 0;
}
user2023370
  • 10,488
  • 6
  • 50
  • 83
0

In this Example,

namespace foo{
  struct bar{
    friend void baz(){}
    void call_friend();
  };
}

int main(){
  foo::baz();           // can't access through enclosing scope of the class
  foo::bar::baz();    // can't access through class scope
}

namespace foo{
  void bar::call_friend(){
    baz();    // can't access through member function
  }
}
  1. foo::baz() is inaccessible because the name baz is not visible in scope of namespace foo. If I remember correctly (§ 3.4/2) applies here.

  2. foo::bar::baz() is inaccessible because friends aren't members of class and the scope of inline friend function is namespace or class in which their definition exists therefore, you can't access them outside of that scope.

If you put the declaration of baz() in foo the name of function baz will be visible in foo and the definition will be looked up in the nested scope of bar.

namespace foo{
  void baz();  // declaration at namespace scope
  struct bar{
    friend void baz(){}
  };

  void call_friend() {
     baz();  // ok
  }
}

int main()
{
    foo::baz(); // ok, bar will be looked up in the nested scope of foo::bar.
}
cpx
  • 17,009
  • 20
  • 87
  • 142
  • Well, but `baz` definitly is defined inside `bar`, so that should be its scope according to you, but it isn't. :( – Xeo Nov 21 '11 at 06:33
-2

I think you are confusing friend and private. By declaring function a friend you are granting it access to your private members, and not grating other functions access to it. Anyway, any member function of a struct is accessible by any object because struct members are public by default.

However, in your case baz isn't accessible because by doing friend void baz(){} you didn't really declare the function baz, you just said that it is a friend function. You can just remove the friend keyword, and it will solve all the issues.

Igor
  • 26,650
  • 27
  • 89
  • 114
  • On your edit: No, I'm surely not confusing the two. And `friend void bar(){}` is a perfectly fine function definition. – Xeo Nov 21 '11 at 05:46
  • 1
    If he removes the friend declaration then it becomes a member rather than a free standing function (which may not be what is wanted here). I think the fact it is a struct is just to simplify the example. – Martin York Nov 21 '11 at 06:04