Friends confusion - c++

$11.4/5 - "[...]A friend function defined in a class is in the (lexical) scope of the class in which it is defined[...]"
What does this statement mean?
struct A{
typedef int MYINT;
void f2(){f();} // Error, 'f' is undefined
friend void f(){MYINT mi = 0;} // Why does this work, shouldn' it be A::MYINT?
void f1(){f();} // Error, 'f' is undefined
};
int main(){}
What is confusing here is that the call to 'f' from 'A::f1' is quiet understandable. However why is call to 'f' from 'A::f2' ill-formed, when a friend is in 'lexical' scope of the befriending class? What does 'lexical' scope mean?
At the same type why is the usage of 'MYINT' in 'f' OK? Shouldn't it be 'A::MYINT'?
If I add a parameter of type 'A *' to 'f', then both 'f1' and 'f2' are able to find 'f' because of ADL. This is understandable.

You have quoted only part of §11.4/5. According to it f() should be declared out of class first (function should have namespace scope). Try this:
void f(); // declare it first
struct A{
typedef int MYINT;
void f2(){f();}
friend void f(){MYINT mi = 0;} // definition of global f, a friend of A
void f1(){f();}
};
As for second question, it is ok because of quoted by you part of §11.4/5. f() obeys the same rules for name binding as a static member function of that class and has no special access rights to members of an enclosing class.

Here is my interpreation of one part of my query which is
"Why MYINT" can be referred to as
"MYINT" instead of "A::MYINT"?
$3.4.1/9 states - "Name lookup for a
name used in the definition of a
friend function (11.4) defined inline
in the class granting friendship shall
proceed as described for lookup in
member function definitions. If the
friend function is not defined in the
class granting friendship, name lookup
in the friend function definition
shall proceed as described for lookup
in namespace member function
definitions."
In our case, the name to be looked up is 'MYINT' which is a unqualified name. The lookup for this name inside the definition of the friend 'f' which is defined inline in the class would would be done in the same was as it would be for member functions of 'A'.
Is my understanding correct?

Related

C++ friend operator definition inside class body serves as function declaration?

I'm a newbie reading the C++ Primer book. It says:
A friend declaration only specifies access. It is not a general declaration of the function. If we want users of the class to be able to call a friend function, then we must also declare the function separately from the friend declaration. To make a friend visible to users of the class, we usually declare each friend (outside the class) in the same header as the class itself.
But I just found that this is not the case for friend operator functions defined inside the class body. In the following code, f could not be found but operator+ is found:
struct X
{
friend void f()
{
// friend functions can be defined in the class
// this does NOT serve as a declaration, even though this is already a definition
// to use this function, another declaration is REQUIRED
}
friend X operator+(const X & x1, const X & x2)
{
// this stuff serves as declaration somehow
return {x1.v + x2.v};
}
void foo()
{
f(); // ERROR: no declaration for f
X tmp = X {1} + X {2}; // CORRECT
}
int v;
};
Could someone tell me what makes this difference?
I am using g++ (Ubuntu 9.3.0-17ubuntu1~20.04) 9.3.0, as a reference.
I have searched many questions involving friend declaration and definition on StackOverflow but did not find an answer on this. Sorry if this question is duplicated.
The difference is the operator+ takes X as parameter, then ADL could find the name; f takes nothing, ADL can't help any.
(emphasis mine)
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.
Even if we define the function inside the class, we must still provide a declaration outside of the class itself to make that function visible. A declaration must exist even if we only call the friend from members of the friendship granting class. This means in your example, you should forward declare the function f as shown below:
//forward declare f
void f();
struct X
{
//no ADL here since this function f has no parameters of type X. So we also need to declare this outside struct X which we did by providing forward declaration
friend void f()
{
}
void foo()
{
f(); // WORKS NOW because of the forward declaration
X tmp = X {1} + X {2}; //operator+ found using ADL
}
//other members here
};
The output of the program can be seen here.
The reason why the call to operator+ works is because of ADL which stands for argument dependent lookup. In particular, operator+ can be found using ADL since it has parameters of type X. So in this case, you do not need to explicitly declare operator+ outside struct X.

Function overload outside class not seen

Consider following code snippet:
enum class Bar {
A
};
void foo(Bar) {}
struct Baz {
void foo() {
foo(Bar::A);
}
};
It fails to compile, the message from gcc 9.2 is:
:12:19: error: no matching function for call to 'Baz::foo(Bar)'
12 | foo(Bar::A);
|
I don't suspect it is a bug since clang 10 also fails. I have two questions regarding this situation:
Where does standard define bahaviour for such overloads?
What are the possible reasons that compiler behaviour is specified that way?
live example
The call to foo inside Baz::foo() will only look up names inside the class. If you mean to use the foo declared outside the class Baz, you need to use the scope-resolution operator, like this:
enum class Bar {
A
};
void foo(Bar) {}
struct Baz {
void foo() {
::foo(Bar::A); // looks up global 'foo'
}
};
Note that the unscoped call to foo fails because there is a Bar::foo that is found in the closest scope. If you name the function differently, then no function is found in Bar, and the compiler will look in the outer scope for the function.
enum class Bar {
A
};
void foo(Bar) {}
struct Baz {
void goo() { // not 'foo'
foo(Bar::A); // this is fine, since there is no 'Bar::foo' to find
}
};
Here's the quote from cppreference for a class definition.
e) if this class is a member of a namespace, or is nested in a class that is a member of a namespace, or is a local class in a function that is a member of a namespace, the scope of the namespace is searched until the definition of the class, enclosing class, or function. if the lookup of for a name introduced by a friend declaration: in this case only the innermost enclosing namespace is considered, otherwise lookup continues to enclosing namespaces until the global scope as usual.
Of course, this only applies to class definitions, but for member functions (which is your example), it says
For a name used inside a member function body, a default argument of a member function, exception specification of a member function, or a default member initializer, the scopes searched are the same as in [class definition], ...
So the same logic applies.
According to the rule of unqualified name lookup, from the standard, [basic.lookup.unqual]/1,
(emphasis mine)
In all the cases listed in [basic.lookup.unqual], the scopes are searched for a declaration in the order listed in each of the respective categories; name lookup ends as soon as a declaration is found for the name.
That means the name foo is found at the class scope (i.e. the Baz::foo itself), then name lookup stops; the global one won't be found and considered for the overload resolution which happens later.
About your 2nd question, functions can't be overloaded through different scopes; which might cause unnecessary confusion and complexity. Consider the following code:
struct Baz {
void foo(int i) { }
void foo() {
foo('A');
}
};
You know 'A' would be converted to int then passed to foo(int), that's fine. If functions are allowed to be overloaded through scopes, if someday a foo(char) is added in global scope by someone or library, behavior of the code would change, that's quite confusing especially when you don't know about the adding of the global one.

Is the friend declaration a real declaration?

C++ Primer says:
It is important to understand that a friend declaration affects access but is not a declaration in an ordinary sense.
So friend declaration should offer access authority only to the friend class/function, it is not a real declaration.
However, I tried this program, it complied successfully and outputs 2 in GCC 5.2.0, what's wrong?
#include <iostream>
class Tmp {
public:
Tmp(int a) : a_(a) {};
private:
int a_;
friend void p(Tmp a) { std::cout << a.a_ << std::endl; }
};
// void p(Tmp a); I commented it, so there is not any declaration statement for p(Tmp a).
int main(void) {
Tmp a(2);
p(a);
return 0;
}
A friend declaration is a real declaration in the technical sense: it is a declaration according to the grammar of the C++ language. The keyword friend is a specifier that modifies a declaration.
If you really want to know what the book means, you should just look at the immediately preceding text.
Classes and nonmember functions need not have been declared before they are used in a friend declaration. When a name first appears in a friend declaration, that name is implicitly assumed to be part of the surrounding scope. However, the friend itself is not actually declared in that scope (§7.2.1, p. 270).
Even if we define the function inside the class, we must still provide a declaration outside the class itself to make that function visible. A declaration must exist even if we only call the friend from members of the friendship granting class.
The friend declaration is different from a typical declaration because most declarations introduce the names they declare into the scope in which they are found, and then those names may be immediately used:
int x; // introduces the name x into this scope
x = 0; // lookup of "x" finds the name just declared
A friend declaration introduces the name declared into the nearest enclosing namespace, not the class in which the friend declaration is found. So it is unusual in that sense. However, even more unusually, the name introduced by a friend declaration is not visible to either qualified or unqualified name lookup until the same name is declared at the nearest enclosing scope.
In other words, you may not be able to start using the name immediately after the friend declaration. This is probably what the book means when it says the friend declaration is not a declaration in the ordinary sense.
Here is a simple example:
#include <cstdio>
class C {
friend void hello() { std::puts("Hello, world!"); }
};
int main() {
hello();
}
This program is ill-formed because hello was not declared in the global namespace before being called. http://coliru.stacked-crooked.com/a/3ae525122312c96c
Your example only happens to work because there is a special rule that argument-dependent lookup finds friends declared inside associated classes even if they haven't been declared at namespace scope yet. In the call p(a), since a has the class type Tmp, the class Tmp is an associated class.
Friend declarations are as much a declaration as any other, as in they introduce a name; perhaps C++ Primer should have chosen its words better.
A friend declaration however is not visible for qualified or unqualified lookup (in the absence of other declarations of the same name). Your example works due to ADL, but as you can see from the following a friend declaration alone will not make the name available for any other kind of lookup:
struct Tmp {
friend void p(Tmp);
friend void q();
};
int main() {
Tmp a;
p(a); // Argument-dependant lookup ==> ok!
q(); // Unqualified lookup ==> nope
::q(); // Qualified lookup ==> also nope
}
test.cpp: In function 'int main()':
test.cpp:9:9: error: 'q' was not declared in this scope
q();
^
test.cpp:10:7: error: '::q' has not been declared
::q();

some friend functions don't follow the rule

For the following snippet:
class A{
friend void f(){};
public:
A(){f();} //error
};
class B{
friend void f(void* ptr){};
public:
B(){f(this);} //no error
};
According to the rule that although friend functions can be defined inside a class, yet they are not visible until they are declared somewhere outside the class scope, the error in the definition of class A is explained.
But I am confused why the snippet for class B doesn't produce the same error as class A's.
Please can anyone tell me about this?
"Not visible" is a bit of an over-simplification. With only an in-class definition, a friend function can't be found by qualified or unqualified lookup, which is why the first snippet fails.
However, it can be found by argument-dependent lookup (ADL), so you can call it with an argument involving a type that's scoped in the same namespace as the function.
In this case, the argument type is B*, scoped in the global namespace. The friend function is scoped in the namespace containing the class that declares it - also the global namespace. So ADL will look in the global namespace for functions called f, find the friend function, and use that.

The scope of in-class-defined friend function?

A few days ago I asked a question about the scope of in-class-defined friend functions (Which scope does an in-class-defined friend function belong to?), and I get to know that the function is in the scope of the enclosing namespace but won't be searchable until explicitly declared outside the class (ADL is an exception).
Today I found some relevant statements in the C++ standard (section 11.3):
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 ]
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).
We can see that there are two scope-related statements here: "has namespace scope" and "is in the (lexical) scope of the class in which it is defined". I'm confused here. If the former relates to my previous question (Which scope does an in-class-defined friend function belong to?), then what does the latter stand for?
A "namespace-scope function" is a function that is a member of a namespace (i.e the "scope" here means the "home scope" of the function).
The later statement links to 3.4.1, which has to say
Name lookup for a name used in the definition of a friend function (11.3) defined inline in the class granting friendship shall proceed as described for lookup in member function definitions.