C++’s friend keyword is a blunt instrument. The standard reference describes it plainly: “A friend is permitted full access to private and protected members.” There is no syntax for “this class can call my flush() method but not touch my internal buffer.” If Bar needs access to two of Client’s ten private functions, the language gives you exactly one tool — declare Bar a friend — and that tool also exposes the other eight.
The Attorney-Client idiom, named by Alan Bolton in 2006, is the standard C++ workaround: a thin proxy class that is the friend, exposing only the slice of Client’s private interface that the friend genuinely needs.
Three things to take away:
friendis all-or-nothing — there is no built-in way to grant access to a single private member.- The Attorney is a friend of the Client, contains only static forwarding functions for the members it wants to expose, and declares its own (narrower) friends.
- The idiom relies on the rule that friendship is not transitive: Attorney’s friends do not inherit Attorney’s access to Client.
The problem friend doesn’t solve
Friendship gets you all of a class or none of it:
class Client {
private:
void doSafeThing(int);
void doSensitiveThing(double);
int internalBuffer[1024];
friend class Bar;
};
class Bar {
void use(Client& c){
c.doSafeThing(7); // intended
c.doSensitiveThing(3.14); // also accessible — intended?
c.internalBuffer[0] = 0; // also accessible — definitely not
}
};
Bar only needs doSafeThing. The friendship grants it everything. As Client evolves — adding members, renaming internals, restructuring storage — every change risks breaking Bar, even if Bar only ever called the one function. The coupling is wider than the contract requires.
A second relevant rule: friendship is not transitive. If Bar is a friend of Client and Baz is a friend of Bar, that does not make Baz a friend of Client. This is the property the idiom turns into a feature.
The Attorney as a narrow proxy
Insert a class between Client and the would-be friend. Make that class — the Attorney — the only friend of Client. Have the Attorney expose just the slice of Client’s interface the friend should see, and declare its own friends:
class Attorney; // forward declaration
class Client {
public:
Client(int initial) : m_value{ initial } {}
private:
void doSafeThing(int n){ m_value += n; }
void doSensitiveThing(double){ /* ... */ }
int m_value;
friend class Attorney; // only Attorney has full access
};
class Attorney {
private:
static void doSafeThing(Client& c, int n){ c.doSafeThing(n); }
// Note: doSensitiveThing is deliberately not exposed.
friend class Bar; // only Bar can use Attorney's API
};
class Bar {
public:
void use(Client& c){
Attorney::doSafeThing(c, 7); // OK
// Attorney::doSensitiveThing(c, 3.14); // not declared — compile error
// c.doSensitiveThing(3.14); // Bar isn't Client's friend — compile error
}
};
Bar can reach exactly one of Client’s private members: doSafeThing. The buffer is unreachable. doSensitiveThing is unreachable. If Client adds a new private member next week, Bar gains no access to it — Attorney has to grow a forwarder for that, and the access slice stays explicit.
Why this works
Three rules of the language make the construction sound:
- Friendship is not transitive.
Attorneyis a friend ofClient, andBaris a friend ofAttorney.Baris not a friend ofClient. The language enforces this without any help from the design. - Attorney’s interface is private. Its forwarding functions are private static members. Only Attorney’s declared friends can call them. No third party can stumble onto the bridge.
- The forwarders are static and inline. Each one takes a
Client&and forwards to a member call. The compiler can inline through the layer, so the abstraction has no runtime cost — the generated code is identical to a direct private- member call.
The result is exactly the contract the original friend class Bar should have expressed: “Bar may invoke doSafeThing, and nothing else.”
Variations
The pattern composes in a few useful ways:
- Multiple Attorneys per Client. When different friends need different slices of the private interface, give each its own Attorney.
EngineAttorneyexposes the engine controls;MaintenanceAttorneyexposes the diagnostic counters. Each friend talks to the Attorney that matches its role, and the slices stay independent. - One Attorney for a hierarchy. A single Attorney class can sit in front of an inheritance hierarchy and expose the same protected/private interface across multiple Clients — particularly useful for testing harnesses that need to peek into protected internals across a family of types without declaring every test class a friend of every base.
- Test-only Attorneys. A
TestAttorneycan expose private state read-only, declaredfriendonly by the test fixture, letting tests assert invariants without making production code’s privacy looser.
When not to use it
Attorney-Client is an idiom, not a default. It costs an extra class per access slice and adds a layer of indirection in the source. If the friend genuinely needs full access — a tightly-coupled iterator, a copy constructor in a related class, an operator<< overload — plain friend is the right tool. If the access can be expressed through the public interface at all, that is better still: the Scott Meyers algorithm for “member, friend, or non-friend free function” explicitly recommends public-interface implementations whenever they suffice.
The idiom earns its keep when (a) you want to grant access to a real subset of private members, (b) the would-be friend is a class you don’t want coupled to the rest of Client’s internals, and (c) the alternative — making those particular members public, or refactoring them out — would weaken the design.
Takeaway
friend in C++ is all-or-nothing, and the Attorney-Client idiom is the standard pattern for granting only the slice of a class’s private interface that a particular friend actually needs. Make the Attorney the sole friend of the Client, give it a private interface of static forwarders for the members you wish to expose, and let the Attorney choose its own friends. The construction is sound because friendship is not transitive, the forwarders inline away to nothing at runtime, and every access slice is named and visible in source. The rule is simple: when the friend would only need part of the private interface, give it an Attorney rather than the keys to the whole house.