any design patterns and programming idioms rely on the use of friend declarations. However, when friend declarations are used in templates, the obfuscated syntax can bewilder even expert programmers. This month’s solution demonstrates how to fine-tune friend declarations, while overcoming the syntax barrier at the same time.
How do you declare a template as a friend of another template? How can you distinguish between a specialization and a primary template in such declarations? What if you need to declare an asymmetric overloaded operator as a friend?
Follow the guidelines and rules set forth in this solution.
Non-template Friends
Let’s start with the basics. Suppose you have a class template A that has to declare:
- an ordinary function f()
- an ordinary class C
as its friends. Friend declarations within a class template look like this:
class C {};void f (int);template class A{public: //... friend class C; friend void f (int);};A xi;A<:string> xs;
In each specialization of the template A, f() and C are friends.
Note that the access specifiers don’t affect friend declarations. Thus, you may move the friend declaration to the private or protected section of the class without changing its semantics in any way. However, the common practice is to declare friends as public.
You should always provide the elaborated type specifiers of the befriended entity in a friend declaration. In other words, the following declaration is invalid, although some compilers may accept them:
template class X{public: //... friend C; //error, missing 'class' before C};
Befriending Templates
Declaring a class template as a friend of another class template is a bit different. In this case, the friend keyword appears after the template declaration:
template class A{/*...*/};template class B{public: //... template friend class A;};
Here, every specialization of A (e.g., A
Sometimes however, you want to restrict friendship to specific specializations of the befriended template. For example, you can allow A
template class A{/*…*/};template class B{public: // only A and A are friends friend class A ; friend class A <:string>;};
Befriending Function Templates
Syntactically speaking, declaring function templates as friends is the most complex part in this trilogy. Let’s look at a concrete example. Suppose you have a class template called Vector. You want to declare the overloaded operator== function template as a friend of Vector. Consequently, for every Vector
First, forward declare both the class template granting friendship and the function template:
template class Vector; // forward declaration of function template operator==template bool operator== (const Vector& v1, const Vector& v2);
Next, declare the function template as a friend inside the class template:
template class Vector{public: friend bool operator== (const Vector& v1, const Vector& v2); };
Finally, define the function template:
template bool operator==(const Vector& v1, const Vector& v2){ //...}Vector vi,vi2;bool b= vi==vi2;
This seems like a lot of work for a single friend declaration. You can collapse these three steps into one by moving the definition of the befriended function into the friend declaration:
template class Vector{public: //defining the friend function template inside the class friend bool operator== (const Vector& v1, const Vector& v2) { //.. }};
In the overloaded operator== and class Vector
You may declare a specialization of a function template as a friend. To do so, forward declare the class template and the function template, as before. Then include a friend declaration inside class Vector:
template class Vector; template bool operator==(const Vector &v1, const Vector& v2 );template class Vector{public: friend bool operator== (const Vector& v1, const Vector& v2); };
The difference between this declaration and the former one is that the template argument (int in this case) appears in angle brackets after the function’s name and instead of the template parameter T, in the function’s parameters list.
Finally, define the specialization of the function template in question:
template bool //specialization definition operator== (const Vector& v1, const Vector& v2){ //..}
Recall that a definition of a specialization is preceded by the sequence template . Note also that a specialization of a function template cannot be defined within a friend declaration.
Aren’t Friends Electric?
We’re reaching the last twist in this thick plot. Suppose you want to define an overloaded == function template that accepts different types of arguments:
// asymmetric, used in Vector == Vector etc.template bool operator== (const Vector &a, const Vector &b);
Such an asymmetric overloaded operator probably reminds you the asymmetric assignment operator discussed here recently. Here again you have two distinct template parameters, T and T2. How do you declare such an asymmetric overloaded operator as a friend? Here’s the answer:
template class Vector { T val;public: explicit Vector(T v = 0): val(0){} ~Vector(){} //asymmetric operator == template friend bool operator==(const Vector &a, const Vector &b);};//definition of asymmetric operator==template bool operator== (const Vector &a, const Vector &b) { return a.val == b.val;}Vector vi, vi2;Vector vd;bool equal = vi == vi2; // OKequal = vi == vd;// OK