Skip to content
C++
Language
since C++98
Intermediate

friend Functions and Classes

C++ friend declarations — friend functions, friend classes, hidden friends, templates, operator overloading, and when friendship is appropriate.

friendsince C++98

A friend declaration inside a class grants a named external function or class full access to all private and protected members of the declaring class.

Overview

friend is a deliberate, named exception to access control. It does not break encapsulation — it precisely extends a class's interface to specific, trusted collaborators. The grantor class controls the relationship: the friend has no say.

Three properties govern friendship:

  • Not inherited. A subclass of a friend does not inherit friendship with the grantor.
  • Not transitive. Friends of friends are not friends.
  • Not symmetric. If A friends B, B does not automatically friend A.

These constraints are intentional. Friendship is a tight coupling and should be narrow in scope.

Syntax

cpp
class Grantor {
    // Friend a free function (declared here, defined outside)
    friend ReturnType function_name(params);

    // Friend a free function defined inline (hidden friend)
    friend ReturnType function_name(params) { /* body */ }

    // Friend an entire class
    friend class FriendClass;

    // Friend a specific class template instantiation
    friend class Container<T>;  // inside a template<typename T> class

    // Friend a function template
    template<typename U>
    friend bool operator==(const Grantor& a, const Grantor& b);
};

Friend declarations may appear anywhere in the class body — their position relative to public/private/protected labels is irrelevant. Convention is to group them near the top of the class, adjacent to the public interface they extend.

Examples

Free Function Friend

cpp
class Vector2 {
    double x_, y_;

public:
    constexpr Vector2(double x, double y) noexcept : x_{x}, y_{y} {}

    friend double dot(const Vector2& a, const Vector2& b) noexcept;
    friend std::ostream& operator<<(std::ostream& os, const Vector2& v);
};

double dot(const Vector2& a, const Vector2& b) noexcept {
    return a.x_ * b.x_ + a.y_ * b.y_;  // accesses private x_, y_
}

std::ostream& operator<<(std::ostream& os, const Vector2& v) {
    return os << '(' << v.x_ << ", " << v.y_ << ')';
}

The function is declared inside the class (granting access) but defined outside. It is a non-member — it is not in the class's scope, does not appear in the class's name lookup, and takes no implicit this.

Friend Class

cpp
class LinkedList;

class Node {
    int value_;
    Node* next_ = nullptr;

    friend class LinkedList;  // LinkedList manipulates internals directly

public:
    explicit Node(int v) : value_{v} {}
};

class LinkedList {
    Node* head_ = nullptr;

public:
    void push_front(int v) {
        Node* n = new Node(v);
        n->next_ = head_;       // OK: Node::next_ is private
        head_ = n;
    }

    int front() const { return head_->value_; }
};

A container/node pair is the canonical use case for friend class. The node's implementation details are entirely encapsulated from external code; only LinkedList (the logical owner) gets access.

Forward-declaring LinkedList before Node is not required here because the friend declaration is not a use of the type — the compiler only needs the name. However, when LinkedList actually accesses Node members, the full definition must be visible.

Hidden Friend (Inline Friend)

Defining the friend inside the class body makes it findable only through Argument-Dependent Lookup (ADL). This is the preferred pattern for operator overloads:

cpp
class Temperature {
    double celsius_;

public:
    explicit Temperature(double c) noexcept : celsius_{c} {}

    // C++20: synthesizes <, <=, >, >= from <=>
    friend auto operator<=>(const Temperature& a, const Temperature& b) noexcept {
        return a.celsius_ <=> b.celsius_;
    }

    friend bool operator==(const Temperature& a, const Temperature& b) noexcept {
        return a.celsius_ == b.celsius_;
    }

    friend std::ostream& operator<<(std::ostream& os, const Temperature& t) {
        return os << t.celsius_ << "°C";
    }
};

Temperature t1{20.0}, t2{25.0};
bool cooler = t1 < t2;  // true — found via ADL on Temperature

The hidden-friend idiom prevents these operators from participating in overload resolution unless at least one argument is a Temperature. This limits namespace pollution and avoids surprising implicit conversions.

Friend Templates: Specific vs. Universal

When templates are involved, the scope of friendship matters significantly.

Universal friendship (all instantiations are friends — usually unintentional):

cpp
template<typename T>
class Box {
    T value_;

public:
    explicit Box(T v) : value_{std::move(v)} {}

    // Friends operator== for ALL Box<U>, not just Box<T>
    template<typename U>
    friend bool operator==(const Box<U>& a, const Box<U>& b);
};

This grants Box<int>'s private members to operator==<double>, which is almost never intended.

Preferred: hidden friend inside the template (friends only the matching instantiation):

cpp
template<typename T>
class Box {
    T value_;

public:
    explicit Box(T v) : value_{std::move(v)} {}

    // Inline friend: each Box<T> instantiation gets its own operator==
    friend bool operator==(const Box& a, const Box& b) {
        return a.value_ == b.value_;
    }

    friend bool operator!=(const Box& a, const Box& b) {  // pre-C++20
        return !(a == b);
    }
};

Box<int> x{42}, y{42};
bool eq = (x == y);  // true; ADL finds the right instantiation

Inside a class template, Box without template arguments refers to the current instantiation. The inline friend here is a non-template function generated per instantiation — exactly what you want.

Testing With Friend

cpp
class RateLimiter {
    int capacity_;
    int tokens_;
    std::chrono::steady_clock::time_point last_refill_;

    void refill() noexcept;

    friend class RateLimiterTest;

public:
    explicit RateLimiter(int capacity);
    bool acquire() noexcept;
};

class RateLimiterTest {
public:
    static void test_partial_consumption() {
        RateLimiter rl{10};
        rl.tokens_ = 3;                        // inject state directly
        assert(rl.acquire());
        assert(rl.tokens_ == 2);               // verify internal state
    }
};

Friending a test class is a pragmatic trade-off: it avoids adding accessors to the production interface solely for testing. The friend declaration is restricted to the test class by name — not a blanket exposure.

Best Practices

Prefer hidden friends for binary operators. Define operator==, operator+, operator<< etc. inline inside the class. ADL finds them when needed; unqualified lookup ignores them. This is the modern idiomatic choice.

Keep friend lists short. More than two or three friend declarations on a single class is a warning sign. Consider whether the design should be restructured.

Place friend declarations at the top of the class body. They are part of the class interface — grouping them with public makes their intent visible.

Friend specific instantiations, not all. In class templates, use inline friends rather than unconstrained template<typename U> friend declarations.

Consider the alternative. Before adding a friend, ask whether a well-designed public interface (or a nested class) can serve the purpose without coupling.

Common Pitfalls

Friendship is one-directional. friend class B inside A lets B access A's privates — not the other way around.

Friend function not found without ADL or forward declaration. A friend function defined only inside the class is invisible to unqualified lookup. It is found solely via ADL. If you call it with non-class arguments, it will not resolve.

cpp
class Tag {};
class Wrapper {
    friend void process(Tag, int x) { /* ... */ }  // hidden friend
};

process(Tag{}, 42);   // OK: ADL on Tag finds process
// process(42, Tag{}); // Error if first arg is int — ADL on int finds nothing

Friendship is not inherited in either direction. A class derived from the grantor does not inherit the friend relationship. A class derived from the friend does not get the friendship. Both must be explicit.

cpp
class Secret {
    int data_ = 0;
    friend class Trusted;
};

class Trusted {};
class DerivedTrusted : public Trusted {};

void f() {
    Secret s;
    // DerivedTrusted cannot access s.data_ — friendship not inherited
}

friend does not imply inline or static. A friend function defined outside the class has normal linkage. A friend function may not be declared static or extern.

Repeated declarations. Declaring the same function as a friend multiple times in the same class is legal but redundant. It does not multiply-grant access.

Friend function templates

A friend declaration can name a function template, but the scope of that friendship must be tightly controlled. The most common mistake is declaring a universally-parameterised friend template when only the matching instantiation is needed.

cpp
// C++11
template<typename T>
class Pair {
    T first_, second_;

public:
    Pair(T a, T b) : first_{std::move(a)}, second_{std::move(b)} {}

    // Correct: inline friend — each Pair<T> instantiation gets its own swap
    friend void swap(Pair& a, Pair& b) noexcept {
        using std::swap;
        swap(a.first_,  b.first_);
        swap(a.second_, b.second_);
    }
};

Pair<int> p{1, 2}, q{3, 4};
swap(p, q);  // found via ADL on Pair<int>

When the friend function itself must be a template (e.g., it needs its own independent type parameter), forward-declare the function template before the class, then friend the specific specialisation:

cpp
// C++11
template<typename T> class Matrix;
template<typename T> Matrix<T> transpose(const Matrix<T>&);

template<typename T>
class Matrix {
    // ...
    friend Matrix<T> transpose<T>(const Matrix<T>&);  // only the T-specialisation
};

This avoids granting transpose<double> access to Matrix<int>.

Friend classes and forward declaration

A friend class declaration introduces the friend's name into the enclosing namespace without requiring its full definition. This is one of the few places in C++ where a bare name like friend class Foo; simultaneously forward-declares Foo and grants it friendship.

cpp
// C++11
class Engine;   // optional explicit forward declaration

class Car {
    int horsepower_ = 0;
    friend class Engine;  // also forward-declares Engine if not already seen

public:
    explicit Car(int hp) : horsepower_{hp} {}
};

class Engine {
public:
    void tune(Car& c) {
        c.horsepower_ += 50;  // OK: Engine is a friend of Car
    }
};

The key caveat: mutual friendship requires two separate declarations. friend class Engine inside Car does not make Car a friend of Engine. Each class must explicitly grant friendship to the other.

cpp
// C++11
class B;

class A {
    int x_ = 1;
    friend class B;  // B can access A::x_
};

class B {
    int y_ = 2;
    friend class A;  // A can access B::y_; this is separate, not automatic

public:
    void read(A& a) { (void)a.x_; }
};

Passkey idiom (controlled friendship)

The passkey idiom uses friendship to create a capability token that restricts which callers can invoke a sensitive constructor or method. It is a modern alternative to friending an entire class for a single operation.

cpp
// C++11
class Factory;

class Widget {
public:
    // Passkey: only constructible by someone who can construct Passkey
    class Key {
        Key() = default;          // private constructor
        friend class Factory;     // only Factory can mint a Key
    };

    // The constructor is public, but callers must supply a Key
    explicit Widget(Key, int value) : value_{value} {}

private:
    int value_;
};

class Factory {
public:
    static Widget make(int v) {
        return Widget{Widget::Key{}, v};  // OK: Factory can construct Key
    }
};

// Widget w{Widget::Key{}, 42};  // Error: Key() is private
Widget w = Factory::make(42);    // OK

The passkey achieves fine-grained access control without giving Factory unrestricted access to all of Widget's internals. Multiple factories can each hold their own key type, limiting cross-contamination.

cpp
// C++17 — variadic passkey with templated token
template<typename... Allowed>
class Passkey {
    Passkey() = default;
    friend Allowed...;   // C++17: pack expansion in friend declaration
};

Friend and operator overloading

Operator overloads that need access to private data but cannot be member functions are the canonical use for friend. operator<< is the textbook example — it must take std::ostream& as its first argument, so it cannot be a member of the user's class.

cpp
// C++11
class Complex {
    double re_, im_;

public:
    constexpr Complex(double r, double i = 0.0) noexcept : re_{r}, im_{i} {}

    // Arithmetic: member is fine when left operand is the class
    constexpr Complex operator+(const Complex& rhs) const noexcept {
        return {re_ + rhs.re_, im_ + rhs.im_};
    }

    // I/O and comparison: hidden friends — ADL-only, no namespace pollution
    friend std::ostream& operator<<(std::ostream& os, const Complex& c) {
        return os << c.re_ << (c.im_ >= 0 ? "+" : "") << c.im_ << 'i';
    }

    friend std::istream& operator>>(std::istream& is, Complex& c) {
        return is >> c.re_ >> c.im_;
    }

    friend constexpr bool operator==(const Complex& a, const Complex& b) noexcept {
        return a.re_ == b.re_ && a.im_ == b.im_;
    }
};

For symmetric binary operators like operator+ where either operand could trigger an implicit conversion, a non-member (hidden) friend beats a member function because both sides go through the same overload resolution:

cpp
// C++11
class Meters {
    double value_;
public:
    explicit Meters(double v) : value_{v} {}

    // Hidden friend: Meters + Meters, but also double-converted variants
    friend Meters operator+(Meters a, Meters b) noexcept {
        return Meters{a.value_ + b.value_};
    }
};

Friend and inheritance: what is not inherited

Friendship is strictly non-transitive and non-heritable — a derived class never automatically inherits the friendship its base class was granted, nor the friendship the grantor class extended. This is a deliberate language rule to keep access control containable.

cpp
// C++11
class BankAccount {
    double balance_ = 0.0;
    friend class Auditor;   // only Auditor gets access
};

class Auditor {
public:
    void inspect(BankAccount& a) { (void)a.balance_; }  // OK
};

class JuniorAuditor : public Auditor {
public:
    void inspect(BankAccount& a) {
        // (void)a.balance_;  // Error: JuniorAuditor is NOT a friend of BankAccount
    }
};

Similarly, a subclass of the grantor does not carry the friend relationship forward:

cpp
// C++11
class Logger {
    std::string log_;
    friend class Printer;
};

class DetailedLogger : public Logger {
    int level_ = 0;
    // Printer is NOT a friend of DetailedLogger unless explicitly declared
};

class Printer {
public:
    void print(Logger& l)         { (void)l.log_; }   // OK
    void print(DetailedLogger& d) {
        (void)d.log_;    // OK: log_ is inherited from Logger, Printer is its friend
        // (void)d.level_;  // Error: level_ belongs to DetailedLogger, not Logger
    }
};

The rule: a friend of the base can access base-class members (because they belong to the base), but not new members introduced in derived classes.

Namespace-scope friend injection (Barton-Nackman trick)

The Barton-Nackman trick (1994) uses inline friend definitions inside a class template to inject non-member operator overloads into the enclosing namespace, making them findable by ADL but not by ordinary unqualified lookup. Before partial specialisation was fully standardised, this was the primary technique for writing arithmetic operators for numeric class templates.

cpp
// C++98 / still valid in C++11 and later
template<typename T>
class Quantity {
    T value_;

public:
    explicit Quantity(T v) : value_{v} {}

    // Injected into the enclosing namespace via the friend definition,
    // but only findable by ADL on Quantity<T>
    friend Quantity operator+(Quantity a, Quantity b) {
        return Quantity{a.value_ + b.value_};
    }

    friend Quantity operator*(Quantity a, T scalar) {
        return Quantity{a.value_ * scalar};
    }

    friend bool operator<(Quantity a, Quantity b) {
        return a.value_ < b.value_;
    }

    friend std::ostream& operator<<(std::ostream& os, Quantity q) {
        return os << q.value_;
    }
};

// Each instantiation Quantity<double>, Quantity<int> etc. injects
// its own set of operators — no ambiguity between them.
Quantity<double> a{3.0}, b{4.0};
auto c = a + b;          // ADL finds operator+ injected by Quantity<double>
auto d = a * 2.0;        // finds operator* injected by Quantity<double>

The trick has two important implications for modern code:

  1. Name visibility is ADL-only. The injected functions cannot be called by unqualified name without a Quantity argument — they are effectively invisible to other overload sets.
  2. Each instantiation is independent. operator+ for Quantity<int> and operator+ for Quantity<double> are distinct non-template functions, so there is no cross-instantiation access problem.
cpp
// C++11 — the same technique underlies hidden-friend best practice today
template<typename T, typename Tag>
class StrongTypedef {
    T value_;

public:
    explicit StrongTypedef(T v) : value_{v} {}
    T get() const { return value_; }

    friend StrongTypedef operator+(StrongTypedef a, StrongTypedef b) {
        return StrongTypedef{a.value_ + b.value_};
    }

    friend bool operator==(StrongTypedef a, StrongTypedef b) {
        return a.value_ == b.value_;
    }
};

using Kilograms = StrongTypedef<double, struct KilogramsTag>;
using Metres    = StrongTypedef<double, struct MetresTag>;

Kilograms k1{10.0}, k2{5.0};
auto k3 = k1 + k2;       // OK: Kilograms + Kilograms
// auto bad = k1 + Metres{1.0};  // Error: no matching operator+

The Barton-Nackman trick is the historical foundation for the modern "hidden friend" recommendation. Understanding it explains why inline friend operators are preferred: they are per-instantiation, ADL-only, and type-safe.

See Also