Difference between a pointer to a standalone and a friend function - c++

I don't understand why the following does not compile (e.g. in gcc 9.10 or MS VS C++ 2019):
class X {
public:
friend bool operator==(int, X const &);
};
int main() {
2 == X(); // ok...
static_cast<bool (*)(int, X const &)>(&operator==); // Error: 'operator==' not defined
return 0;
}
but the following code is compiled without any issues:
class X {
public:
};
bool operator==(int, X const &);
int main() {
2 == X(); // ok...
static_cast<bool (*)(int, X const &)>(&operator==); // OK!
return 0;
}
My expectation is that a friend function (operator==) of X behaves as a standalone function (operator==). What I'm missing? Thanks

What I'm missing?
An inline friend declaration does not make the function available to ordinary name lookup.
Pay close attention to the error. It does not say that the function is of the wrong type, it simply can't find anything named operator==. This is by design.
Inline friend definitions are only found by argument dependent lookup. Ordinary lookup (such as naming the function to take its address), cannot find it. If you want the function to be available for that purpose, you must provide a namespace scoped declaration.
class X {
public:
friend bool operator==(int, X const &) { /* ... */ }
};
bool operator==(int, X const &);

From the standard 11.9.3.7:
Such a function is implicitly an inline ([dcl.inline]) function if it is attached to the global module. 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 ([basic.lookup.unqual]).
From the standard namespace.memdef/3:
(Thanks #StoryTeller)
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]). [ Note: The name of the friend will be visible in its namespace if a matching declaration is provided at namespace scope (either before or after the class definition granting friendship). — end note ] If a friend function or function template 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 ([basic.lookup.argdep]). 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.
The following doesn't work because the function is not visible in the current scope.
static_cast<bool (*)(int, X const &)>(&operator==); // Error: 'operator==' not defined

The difference is that in the first snippet you only declare the operator in the scope of X, but not outside. There is no operator==(int,X const &) accesible from main. If you fix that and do declare it also outside you only get warnings:
class X {
public:
friend bool operator==(int, X const &);
};
bool operator==(int,X const&); // <--
int main() {
2 == X(); // ok...
static_cast<bool (*)(int, X const &)>(&operator==); // Error: 'operator==' not defined
return 0;
}
Note however, that for both cases you do need a definition to actually call the operator.
For illustration consider that with
struct foo {
friend void bar() {
std::cout << "this is a inline definition of friend funtion";
}
};
The only way to access bar from outside foo is to add a declaration outside of foo:
void bar();

Related

Difference between lookup rules for friend function defined inside vs outside of the class

The following code:
struct X {
X() {}
};
struct Y {
Y() {}
Y(X) {}
Y(int) {}
friend bool operator==(const Y&, const Y&) { return false; }
};
bool f()
{
return 1 == X();
}
fails to compile with the following error:
error: no match for 'operator==' (operand types are 'int' and 'X')
return 1 == X();
While if I move definition of operator== outside of the class it works just fine:
struct X {
X() {}
};
struct Y {
Y() {}
Y(X) {}
Y(int) {}
friend bool operator==(const Y&, const Y&);
};
inline bool operator==(const Y&, const Y&) { return false; }
bool f()
{
return 1 == X();
}
Could someone explain why? (Ideally, with some quote from the standard and human-readable explanation/motivation.) In the answer here: https://stackoverflow.com/a/20114792/1350936 #rightfold mentioned that
Functions defined outside of the class can be found even without ADL
But I don't quite understand what it means.
One thing to note is that the lookup rules for friend functions inside and outside the class are different, see [namespace.memdef] (emphasis mine)
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 or
qualified lookup. [ Note: The name of the friend will be visible in
its namespace if a matching declaration is provided at namespace scope
(either before or after the class definition granting friendship).
— end note] If a friend function or function template 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 ([basic.lookup.argdep]). 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. [  Note: The other
forms of friend declarations cannot declare a new member of the
innermost enclosing namespace and thus follow the usual lookup rules.
— end note ]
That means that in your first example the compiler sees a comparison with operands int and X but there is no viable conversion from X to int (or from int to X but X does not have a comparison operator either). A conversion of both operands to Y is not attempted, because the matching comparison operator is not visible as per the clause quoted above.
At the same time you can see how dangerous it is to have non-explicit constructors in the second example, because both operands are implicitly converted to a possibly unrelated type Y. This might yield very unexpected behaviour because code which should have not compiled due to semantic incorrectness is considered valid by the compiler. See also the C++ Core Guideline C.46: By default, declare single-argument constructors explicit.

Is ADL the only way to call a friend inline function?

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?
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).
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.
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

The member function Outer::f() is not a friend of class Outer::Inner. Why?

According to clang, gcc and vs2013, the function Outer::f is not a friend of the class Outer::Inner.
struct Outer {
void f() {}
class Inner {
friend void f();
static const int i = 0;
};
};
void f() { int i = Outer::Inner::i; }
From [namespace.memdef]/3 I would expect the function Outer::f to be a friend of Outer::Inner, instead of ::f, because the friend declaration is not the first in its namespace containing the name f.
[namespace,memdef]/3 (emphasis is mine):
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, function, class template or function
template97 the friend is a member of the innermost
enclosing namespace. The friend declaration does not by itself make
the name visible to unqualified lookup (3.4.1) or qualified lookup
(3.4.3). [ Note: The name of the friend will be visible in its
namespace if a matching declaration is provided at namespace scope
(either before or after the class definition granting friendship). —
end note ] If a friend function or function template 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.
The first part of the standard that you quoted says (emphasis mine):
Every name first declared in a namespace is a member of that namespace. If a friend declaration in a nonlocal class first declares a class or function the friend class or function is a member of the innermost enclosing namespace.
You are assuming a class is the same as a namespace, which is not correct.
namespace Outer {
void f();
class Inner {
friend void f();
static const int i = 0;
};
}
void Outer::f() { int i = Outer::Inner::i; }
should work. To use the class member function as the friend, you'll have to use:
struct Outer {
void f();
class Inner {
friend void Outer::f();
static const int i = 0;
};
};
void Outer::f() { int i = Outer::Inner::i; }
According to [namespace.memdef]:
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.
What does "outside" mean? It could mean (1) external of (as in, all scopes within the innermost enclosing namespace are permitted, but no others) or it could mean (2) exclusive of (as in, only the innermost enclosing namespace is considered). The wording is potentially ambiguous. However, consider this example which is merged from OP's original question and OP's comments:
struct Outer {
void f() { }
class C { void foo(); };
class Inner {
friend class C;
friend void f();
static const int i = 0;
};
};
void f() { (void)Outer::Inner::i; } // compiles on GCC,Clang
void Outer::C::foo() { (void)Outer::Inner::i; } // compiles on GCC,Clang
int main() { }
Based on wording (1), Outer::f and Outer::C should be friends of Inner. Based on wording (2), ::f and ::C should be the friends. One or the other interpretation could make sense, however both GCC and Clang end up with ::f and Outer::C as the friends, which clearly doesn't make any sense. I have filed GCC Bug 66836 and Clang Bug 24088. So either both compilers are wrong in one direction or another, or there's some part of the standard that explains this logic that definitely escapes me. I wouldn't bet against the latter.

Friend function is not visible in the class

I have the following code:
struct M {
friend void f() {}
M() {
f(); // error: 'f' was not declared in this scope
}
};
int main() {
M m;
}
Live example
Both g++4.8 and clang3.4 fail to compile it, because f is not visible inside M, or so they say.
However, the Standard gives an example of a similar code
class M {
friend void f() { } // definition of global f, a friend of M,
// not the definition of a member function
};
and says that
A friend function defined in a class is in the (lexical) scope of the
class in which it is defined.
(ISO/IEC 14882:2011 11.3 Friends [class.friend] p6, p7)
From this I can't understand how compiler can't find f which is defined in same class where it's used.
It's kinda unlikely that both compilers have the same bug.
So, what did I miss?
The friend declaration states that a function called f in the surrounding namespace is a friend of the class; but it does not introduce the name f into the namespace. It's not available (except by argument-dependent lookup) until it's been declared in the namespace.
The relevant rule is C++11 7.3.1.2/3:
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 or by qualified lookup until a matching declaration is provided in that namespace scope.
This quote from the C++ Standard
A friend function defined in a class is in the (lexical) scope of the
class in which it is defined.
means the following
9 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.
That is any name used in the function is searched starting from the class scope.
However the function itself is not visible in the namespace until it will be declared outside the class.
So in your case it is enough to declare the function before the class definition
void f() {}
struct M {
friend void f();
M() {
f();
}
};
int main() {
M m;
}
Or
void f();
struct M {
friend void f() {}
M() {
f();
}
};
int main() {
M m;
}
The critical question is under what circumstances is the compiler able/allowed to find your function declaration.
For a general friend function, you have to declare it outside of the class such that the compiler is able to find it.
However there is a very useful exception: If the the friend function has an argument of class type, it is able to find the function without additional declaration due to argument-dependent name lookup.
This case is actually very important because normally you would want a friend function to access an object of class type.
Consider the following example:
#include <iostream>
struct M
{
friend void printI(int a) {
std::cout << a;
}
friend void print(const M& m) { // friend takes object of class type!
std::cout << "M";
}
void foo() {
printI(2); // ERROR - requires declaration!
print(*this); // OK!
}
};
int main()
{
M m;
m.foo();
printI(2); // ERROR - requires declaration!
print(m); // OK
}
struct M {
friend void f() {}
M() {
f(); // error: 'f' was not declared in this scope
}
};
int main() {
M m;
}
The above code works perfectly.(tried on DevC++)
Also try not to define the function inside the class as it might not have a scope outside it i.e. in main().
In trying to call f() from main() you'll receive an error saying function doesn't exist.
Therefore, define function outside classes using :: operator (if necessary) so that there is no problem accessing the function from anywhere.
Access friend function defined in class

What's the scope of inline friend functions?

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
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.
"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:
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.
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 :) .
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).
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;
}
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
}
}
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.
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.
}
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.