devxlogo

Taming Friends for Use in Templates

Taming Friends for Use in Templates

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, A, 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 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 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, the compiler shall generate a matching operator==. Such a declaration is done in three steps.

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, the parameter T co-varies, which means that the compiler generates operator== for Vector, operator== for Vector, 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  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 

devxblackblue

About Our Editorial Process

At DevX, we’re dedicated to tech entrepreneurship. Our team closely follows industry shifts, new products, AI breakthroughs, technology trends, and funding announcements. Articles undergo thorough editing to ensure accuracy and clarity, reflecting DevX’s style and supporting entrepreneurs in the tech sphere.

See our full editorial policy.

About Our Journalist