Friendship in C++

Encapsulation is a staple of object-oriented  programming: by hiding (or “abstracting away”) the implementation details of a class behind a public interface we can create abstract data types. Users of an encapsulated class need only be concerned about what operations that type supports and not about how things are actually implemented inside.

C++ supports encapsulation with access specifiers: the keywords public, protected and private are used inside a class definition to indicate which part of a program can access a particular member of a class. In general, members declared under the public access specifier are part of the interface for that type, and can be accessed from anywhere in a program; protected and private access specifiers restrict accessibility of members to derived classes and members of the class itself, respectively.

There are some cases though when we need to access the implementation details of a class from outside, for example when a non member function is part of the public interface of that class (a notable example being overloaded operator functions defined as non member functions). Friendship declarations let non member functions, member functions from other classes, or even entire classes access protected and private members of a class.

Friend functions, friend classes and friend member functions

A class can let other classes access its private and protected members by making those classes friends with a friend declaration inside the class definition:

Friend declarations are not subject to access control, so they can be put anywhere in a class definition. It’s a good practice to put all friend declarations at the beginning or at the end of the class definition. Also note that in order to declare a class as a friend, that class dont’ need to be already visible.

Keep in mind that friendship works in one way only: a class that is granted friendship can access private and protected members of the class containing the friendship declaration, but not vice-versa (unlike the real world, in C++ friendship is not reciprocal).

A class can also declare a function as friend: as said, this is useful when a function is part of the class interface, so it needs to access the class implementation details, but it is not defined as a member function. Think about an overloaded << operator that writes to an output stream: the function is indeed part of the class interface, but it cannot be defined as a member function because the first operand must be a reference to an output stream (the first implicit parameter of an overloaded operator defined as a member function is the implicit “this” pointer). The function is then defined as a non member function and is declared a friend of the class, so it’s able to access the class implementation:

Friend function declaration are not declarations in the general sense, so a declaration for the function must be visible when the user calls the function. Some compilers don’t enforce this rule, and use the friend declaration as a function declaration, but is a good habit to put a declaration for friend functions, should we build our program with a more strict compiler.

As shown in the example above, friend function can even be defined inside the class: in this case it’s as if the function was defined as a non member function outside the class. A declaration outside the class is still needed though.

If we don’t want all members of a class to be declared as friends, we can also restrict access to one or more member functions of a class, by declaring a friend member function. To do so, a declaration for the member function must be in scope, so we must take care to order our code appropriately: first we need to forward declare the class granting friendship; then we provide the class definition for the class that contains the declaration (but not the definition) for the member function to be declared as friend; then we define the class granting friendship, and put the friend declaration there; last, in the implementation file, we define the friend member function:

Friend classes and templates

As usual, when templates get into the mix things get a tad more complicated: a class granting friendship can be a template or not, and the class or function being granted friendship can be a template or not.

When a class template contains a friend declaration for an ordinary (non template) class, all istances of the class template make that class a friend. As usual, ordinary classes don’t need to be visible before being granted friendship:

When both classes are templates things are different: a declaration for the class template that is to be made a friend must be visible and the class template which contains the friendship declaration can choose which instantiation of the friend class template to declare as friend; even the class template parameters can partecipate in specifying the instance of the friend class template:

In this example, a class instantiated with int is friend, and a class instantiated with the same type argument as the granting class’ type parameter is a friend.

A class template can also choose to be friend with all instantiations of the friend class template (for every template arguments with which it can be instantiated) with a friend template declaration:

All instances of the friend class template are friends of every instance of the class template granting friendship. This, for example,  allows the definition of a copy constructor (often called generalized copy constructor) that accepts all instances of the same class template (of course, in this case, the data member must be implicitly convertible). A similar copy assignment operator could be defined as well. Note that the template parameter’s name inside the template parameter list of the friend template is optional.

This applies to ordinary (non template) classes as well: an ordinary class can grant friendship to a particular instance of a template class, or to all instantiations (with a friend template declaration).

Friend functions and templates

A class template can grant friendship to ordinary functions and function templates:

There can be three cases:

  • the friend declaration declares a function name with angle brackets (can be qualified with the scope resolution operator :: or not): the friend function is a function template and the arguments in the angle brackets specify for which instantiation the declaration is valid. If the class granting friendship is a template, its template parameters can be used as template arguments to identify the instance to be made friend; the brackets can also be left empty if template arguments can be deduced. These friend declarations cannot be definitions. A declaration for function templates to be declared as friends must be visible.
  • the name of the function in the friend declaration has no angle brackets and isn’t qualified (doesn’t contain ::): the function never refers to a function template and if no matching ordinary function is already declared the friend declaration is a new declaration. These declarations can be definitions.
  • the name of the function in the friend declaration has no angle brackets but is qualified (contains ::): the name must refer to a previously declared function or function template (an ordinary function is preferred over a matching function template); such friend declarations cannot be definitions.

A member function of a class template can be made friend too (code must be ordered properly):

A class can grant friendship to all instantiation of a function template or to  every instantiation of a member function of a class template using a friend template declaration:

Friend templates can be used in ordinary classes too.

 

 

 

Leave a Reply

Your email address will not be published. Required fields are marked *