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.

See Also