Special Friendship of Templates

Understand and master the complex relationship between C++ friend declarations and template instantiations.

Modern C++ Templates Encapsulation Pitfalls


Table of Contents

  1. Special Friendship of Templates
    1. Table of Contents
    2. Introduction
    3. 1. Non-Template Class with a Template Friend (One-to-Many)
      1. Concept
      2. Code Implementation
    4. 2. Template Class with a Non-Template Friend
      1. Concept
      2. Code Implementation
    5. 3. Template Class with a Specific Template Friend (One-to-One / Bound Friendship)
      1. Concept
      2. Code Implementation
    6. 4. Template Class with an Unbound Template Friend (Many-to-Many)
      1. Concept
      2. Code Implementation
    7. Summary Matrix

Introduction

In standard C++, a friend declaration allows an outside class or function to access private and protected members of a class[cite: 1]. However, when templates enter the picture, configuring friendship becomes significantly more complex[cite: 1].

Depending on software architecture needs, friendship can be established as one-to-one, one-to-many, or non-template-to-template relationships[cite: 1]. Understanding these patterns is essential for maintaining proper encapsulation while leveraging the flexibility of C++ templates[cite: 1].


1. Non-Template Class with a Template Friend (One-to-Many)

Concept

A standard, non-template class can grant friendship to a function or class template[cite: 1]. This grants all instantiations of that template full access to the non-template class’s private members[cite: 1].

Code Implementation

class SecretBank {
private:
    int secretCode = 1234;

    // Grants friendship to ANY instantiation of Auditor<T>
    template <typename T>
    friend class Auditor;
};

template <typename T>
class Auditor {
public:
    void inspect(SecretBank bank) {
        // Works for Auditor<int>, Auditor<std::string>, etc.
        std::cout << bank.secretCode << '\n'; 
    }
};

2. Template Class with a Non-Template Friend

Concept

A class template can grant friendship to a standard, non-template function or class[cite: 1]. Every instantiation of the class template will trust that single, concrete entity[cite: 1].

Code Implementation

void globalPrinter(); // Forward declaration

template <typename T>
class DataBox {
private:
    T data;

    // Standard non-template function is a friend to ALL DataBox<T> instantiations
    friend void globalPrinter(); 
};

3. Template Class with a Specific Template Friend (One-to-One / Bound Friendship)

Concept

This is the most common pattern when writing template classes (such as custom container types or math matrices)[cite: 1]. You want DataBox<int> to be friends only with printData<int>, but not with printData<double>[cite: 1].

Because the friend template parameter matches the class template parameter (T), you must forward-declare both the friend template and the class template beforehand[cite: 1].

Code Implementation

// 1. Forward declarations required
template <typename T> class DataBox;
template <typename T> void printData(const DataBox<T>& box);

// 2. Class template definition
template <typename T>
class DataBox {
private:
    T value;

public:
    DataBox(T v) : value(v) {}

    // 3. Bound Friendship: Notice the '<T>' after printData!
    // DataBox<int> is ONLY friends with printData<int>
    friend void printData<T>(const DataBox<T>& box);
};

template <typename T>
void printData(const DataBox<T>& box) {
    std::cout << box.value << '\n'; // Accesses private 'value'
}
The Pitfall & Trap to Avoid

The Trap: If you forget the <T> specialization syntax in friend void printData<T>(...), the compiler will assume you are declaring a brand-new, standard non-template function rather than referencing the template function[cite: 1].

This subtle error manifests at link time as a Linker Error (Undefined Reference) because the compiler expects a non-template function definition that does not exist[cite: 1].


4. Template Class with an Unbound Template Friend (Many-to-Many)

Concept

If you want every instantiation of a template class to be friends with every instantiation of a friend template (regardless of types), you declare a nested template inside the friend declaration[cite: 1].

Code Implementation

template <typename T>
class DataBox {
private:
    T value;

public:
    DataBox(T v) : value(v) {}

    // Unbound Friendship: Uses a DIFFERENT template parameter 'U'
    // DataBox<int> is friends with Converter<double>, Converter<std::string>, etc.
    template <typename U>
    friend class Converter;
};

Summary Matrix

The following table summarizes the key patterns for template friendship[cite: 1]:

Friendship Type Syntax Inside Class Scope of Access
Bound (One-to-One) friend void func<T>(...); Class<X> is friends only with func<X>[cite: 1].
Unbound (Many-to-Many) template <typename U> friend class FriendClass; Class<X> is friends with FriendClass<Y> for all types Y[cite: 1].
Non-Template Friend friend void func(...); Single non-template function accesses all Class<T> instantiations[cite: 1].

This site uses Just the Docs, a documentation theme for Jekyll.