A friend function named test() is defined inside a template class A:
template <typename T> class A {
public:
friend void cs() {/* code */}
}
Another class inherits from template class A:
class B : public A<B> {}
In main function, I failed to call cs(), the compiler can not see its declaration if I don't provide a function declaration in the global scope:
int main(){
cs()
}
But things are different when cs takes its template class T as an argument:
template <typename T> class A{
public:
friend void cs(const T& t) {}
}
Now cs() can be successfully called in the main function without any decalration:
int main(){
B b;
cs(b);
}
If a function takes a user-defined class as its argument, I know the compiler would search the scope of the user-defined class. So which scope exactly is cs() defined? How it is possible that cs() is successfully called in the second case?
Friend functions can be defined (given a function body) inside class declarations. These functions are inline functions. Like member inline functions, they behave as though they were defined immediately after all class members have been seen, but before the class scope is closed (at the end of the class declaration).
A template friend declaration can name a member of a class template A, which can be either a member function or a member type (the type must use elaborated-type-specifier).
Defining a Function Template A function template starts with the keyword template followed by template parameter(s) inside <> which is followed by the function definition. In the above code, T is a template argument that accepts different data types ( int , float , etc.), and typename is a keyword.
A friend function of a class is defined outside that class' scope but it has the right to access all private and protected members of the class. Even though the prototypes for friend functions appear in the class definition, friends are not member functions.
If a function takes a user-defined class as its argument, I know the compiler would search the scope of the user-defined class.
Yes, it's called ADL. When you write cs(b)
, the compiler searches for a function named cs
. It can't find any, which is what is happening in your first example.
But now since cs
takes a b
, the compiler can also search b
's scope for a function named cs
. That wasn't possible in the first example because there were no parameters! In B
it finds void cs(const B&)
and so uses that.
So which scope exactly is cs() defined?
In the global scope but cs
is not visible to name lookup without ADL.
How it is possible that cs() is successfully called in the second case?
As explained, cs
can be found through ADL on B
.
Note that the name cs
introduced by friend declaration is not visible to ordinary name lookup; that's why the 1st case fails unless you provide a declaration at namespace scope.
A name first declared in a friend declaration within class or class template X becomes a member of the innermost enclosing namespace of X, but is not visible for lookup (except argument-dependent lookup that considers X) unless a matching declaration at the namespace scope is provided
And the name cs
is found successfully in the 2nd case because of ADL; you specify a parameter for cs
.
Argument-dependent lookup, also known as ADL, or Koenig lookup, is the set of rules for looking up the unqualified function names in function-call expressions, including implicit function calls to overloaded operators. These function names are looked up in the namespaces of their arguments in addition to the scopes and namespaces considered by the usual unqualified name lookup.
And
So which scope exactly is cs() defined?
The name cs
become members of the innermost enclosing namespace of A
, i.e. the global namespace here, but it's not visible for ordinary name lookup.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With