8.4 Friends

Ru-Brd

8.4 Friends

The basic idea of friend declarations is a simple one: Identify classes or functions that have a privileged connection with the class in which the friend declaration appears. Matters are somewhat complicated, however, by two facts:

  1. A friend declaration may be the only declaration of an entity.

  2. A friend function declaration can be a definition.

Friend class declarations cannot be definitions and therefore are rarely problematic . In the context of templates, the only new facet of friend class declarations is the ability to name a particular instance of a class template as a friend:

 template <typename T>  class Node;  template <typename T>  class Tree {      friend class Node<T>;   }; 

Note that the class template must be visible at the point where one of its instances is made a friend of a class or class template. With an ordinary class, there is no such requirement:

 template <typename T>  class Tree {      friend class Factory;  // OK, even if first declaration of  Factory      friend class class Node<T>;  // ERROR if  Node  isn't visible  }; 

Section 9.2.2 on page 125 has more to say about this.

8.4.1 Friend Functions

An instance of a function template can be made a friend by making sure the name of the friend function is followed by angle brackets. The angle brackets can contain the template arguments, but if the arguments can be deduced , the angle brackets can be left empty:

 template <typename T1, typename T2>  void combine(T1, T2);  class Mixer {      friend void combine<>(int&, int&);  // OK:  T1 = int&  ,  T2 = int&      friend void combine<int, int>(int, int);  // OK:  T1 = int  ,  T2 = int      friend void combine<char>(char, int);  // OK:  T1 = char T2 = int      friend void combine<char>(char&, int);  // ERROR: doesn't match  combine()  template  friend void combine<>(long, long) {   }  // ERROR: definition not allowed!  }; 

Note that we cannot define a template instance (at most, we can define a specialization), and hence a friend declaration that names an instance cannot be a definition.

If the name is not followed by angle brackets, there are two possibilities:

  1. If the name isn't qualified (in other words, it doesn't contain a double colon ), it never refers to a template instance. If no matching nontemplate function is visible at the point of the friend declaration, the friend declaration is the first declaration of that function. The declaration could also be a definition.

  2. If the name is qualified (it contains :: ), the name must refer to a previously declared function or function template. A matching function is preferred over a matching function template. However, such a friend declaration cannot be a definition.

An example may help clarify the various possibilities:

 void multiply (void*);  // ordinary function  template <typename T>  void multiply(T);  // function template  class Comrades {      friend multiply(int) {}  // defines a new function  ::multiply(int)      friend ::multiply(void*);  // refers to the ordinary function above;   // not to the  multiply<void*>  instance  friend ::multiply(int);  // refers to an instance of the template  friend ::multiply<double*>(double*);  // qualified names can also have angle brackets   // but a template must be visible.  friend ::error() {}  // ERROR: a qualified friend cannot be a definition  }; 

In our previous examples, we declared the friend functions in an ordinary class. The same rules apply when we declare them in class templates, but the template parameters may participate in identifying the function that is to be a friend:

 template <typename T>  class Node {      Node<T>* allocate();   };  template <typename T>  class List {      friend Node<T>* Node<T>::allocate();   }; 

However, an interesting effect occurs when a friend function is defined in a class template because anything that is only declared in a template isn't a concrete entity until the template is instantiated . Consider the following example:

 template <typename T>  class Creator {      friend void appear() {  // a new function  ::appear()  , but it doesn't   // exist until  Creator  is instantiated  }  };  Creator<void> miracle;  //  ::appear()  is created at this point  Creator<double> oops;  // ERROR:  ::appear()  is created a second time!  

In this example, two different instantiations create two identical definitions ”a direct violation of the ODR (see Appendix A).

We must therefore make sure the template parameters of the class template appear in the type of any friend function defined in that template (unless we want to prevent more than one instantiation of a class template in a particular file, but this is rather unlikely ). Let's apply this to a variation of our previous example:

 template <typename T>  class Creator {      friend void feed(Creator<T>*){  // every  T  generates a different   // function  ::feed()      }  };  Creator<void> one;  // generates  ::feed(Creator<void>*)  Creator<double> two;  // generates  ::feed(Creator<double>*) 

In this example, every instantiation of Creator generates a different function. Note that even though these functions are generated as part of the instantiation of a template, the functions themselves are ordinary functions, not instances of a template.

Also note that because the body of these functions is defined inside a class definition, they are implicitly inline. Hence, it is not an error for the same function to be generated in two different translation units. Section 9.2.2 on page 125 and Section 11.7 on page 174 have more to say about this topic.

8.4.2 Friend Templates

Usually when declaring a friend that is an instance of a function or a class template, we can express exactly which entity is to be the friend. Sometimes it is nonetheless useful to express that all instances of a template are friends of a class. This requires a so-called friend template . For example:

 class Manager {      template<typename T>          friend class Task;      template<typename T>          friend void Schedule<T>::dispatch(Task<T>*);      template<typename T>          friend int ticket() {              return ++Manager::counter;          }      static int counter;  }; 

Just as with ordinary friend declarations a friend template can be a definition only if it names an unqualified function name that is not followed by angle brackets.

A friend template can declare only primary templates and members of primary templates. Any partial specializations and explicit specializations associated with a primary template are automatically considered friends too.

Ru-Brd


C++ Templates
C++ Templates: The Complete Guide
ISBN: 0201734842
EAN: 2147483647
Year: 2002
Pages: 185

flylib.com © 2008-2017.
If you may any questions please contact us: flylib@qtcs.net