刘卫东
刘卫东

Reputation: 325

friend function defined inside a template class

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?

Upvotes: 5

Views: 151

Answers (3)

songyuanyao
songyuanyao

Reputation: 173044

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.

Upvotes: 3

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?

You are describing argument dependent lookup here. And that's exactly how cs is found.

cs is in the same namespace that A<B> is defined it, i.e, the namespace and scope of A.

For the purposes of ADL, an implementation collects "associated namespaces". Those include the namespaces of base classes too.

[basic.lookup.argdep]

2.2 If T is a class type (including unions), its associated classes are: the class itself; the class of which it is a member, if any; and its direct and indirect base classes. Its associated namespaces are the innermost enclosing namespaces of its associated classes. Furthermore, if T is a class template specialization, its associated namespaces and classes also include: the namespaces and classes associated with the types of the template arguments provided for template type parameters (excluding template template parameters); the namespaces of which any template template arguments are members; and the classes of which any member templates used as template template arguments are members. [ Note: Non-type template arguments do not contribute to the set of associated namespaces. — end note ]

Upvotes: 1

Rakete1111
Rakete1111

Reputation: 49028

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.

Upvotes: 6

Related Questions