DevX HomePage

Taming Friends for Use in Templates

You can declare classes and functions friends, but the unfriendly syntax may bewilder you—particularly if you're using them in templates. Learn some simple guidelines and rules to tame friend declarations and make them do precisely what you want them to.
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:

as its friends. Friend declarations within a class template look like this:

class C {};
void f (int);

template <typename T> class A
{
public:
  //...
  friend class C;
  friend void f (int);
};
A <int> xi;
A<std::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 <typename T> 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 <typename U> class A{/*...*/};

template <typename T> class B
{
public:
  //...
 template <typename U> friend class A;
};
Here, every specialization of A (e.g., A<int>, A<bool>, etc.) is a friend of every specialization of B.

Sometimes however, you want to restrict friendship to specific specializations of the befriended template. For example, you can allow A<int> to be a friend of every specialization of B, while denying friendship from all other specializations of A. In order to grant friendship selectively, declare the befriended specialization(s) as follows:


template <class U> class A{/*…*/};

template <class T> class B
{
public:
 
 // only A<int> and A<string> are friends 
 friend class A <int>;
 friend class A <std::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<T>, the compiler shall generate a matching operator==<T>. Such a declaration is done in three steps.

First, forward declare both the class template granting friendship and the function template:


template <typename T> class Vector; 
// forward declaration of function template operator==

template <typename T> 
 bool operator== (const Vector<T>& v1, 
                  const Vector<T>& v2);
Next, declare the function template as a friend inside the class template:

template <typename T> class Vector
{
public:
 friend bool operator==<T> (const Vector<T>& v1, 
                            const Vector<T>& v2); 
};
Finally, define the function template:

template <typename T> 
 bool operator==(const Vector<T>& v1, 
                 const Vector<T>& v2)
{  
//...
}

Vector<int> 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 <typename T> class Vector
{
public:
 //defining the friend function template inside the class
 friend bool operator== (const Vector<T>& v1, 
                         const Vector<T>& v2)
 {
   //..
 }
};
In the overloaded operator== and class Vector<T>, the parameter T co-varies, which means that the compiler generates operator==<int> for Vector<int>, operator==<bool> for Vector<bool>, etc. Don't let the syntax mislead you though: a function defined within a friend declaration is never a member function of the enclosing class. It's an ordinary freestanding function.

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 <typename T> class Vector;  

template <typename T> 
  bool operator==(const Vector<T> &v1, 
                  const Vector<T>& v2 );

template <typename T> class Vector
{
public:
 friend bool operator==<int> (const Vector<int>& v1,             
                              const Vector<int>& 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==<int> (const Vector<int>& v1, 
                  const Vector<int>& 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<int> == Vector<double> etc.

template <typename T, typename T2> 
 bool operator== (const Vector<T> &a, 
                  const Vector<T2> &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 <typename T> class Vector {
 T val;
public:
 explicit Vector(T v = 0): val(0){}
 ~Vector(){}
 
  //asymmetric operator ==
 template <typename T2> 
  friend bool operator==(const Vector <T> &a, 
                         const Vector <T2> &b);
};

//definition of asymmetric operator==

template <typename T, typename T2> 
 bool operator== (const Vector<T> &a,                  
                  const Vector<T2> &b) 
{
 return a.val == b.val;
}


Vector<int> vi, vi2;
Vector<double> vd;

bool equal = 
 vi == vi2; // OK
equal = 
 vi == vd;// OK 

Danny Kalev is a certified system analyst and software engineer specializing in C++ and the theoretical aspects of formal languages. He is the author of Informit C++ Reference Guide and The ANSI/ISO Professional C++ Programmer's Handbook. He was a member of the C++ standards committee between 1997 and 2000. Danny recently finished his MA in general linguistics summa cum laude. In his spare time he likes to listen to classical music, read Victorian literature, and explore natural languages such as Hittite, Basque, and Irish Gaelic. Additional interests include archeology and geology. He also gives lectures about programming languages and applied linguistics at academic institutes.


DevX is a division of Jupitermedia Corporation
© Copyright 2007 Jupitermedia Corporation. All Rights Reserved. Legal Notices