Scott Meyers wrote an article in CUJ a few years ago entitled How Non-Member Functions Improve Encapsulation. I highly recommend this article, as well as Herb Sutter’s GotW #84: Monoliths “Unstrung”, in which everyone’s favorite scapegoat, std::string, is refactored using this principle.
I have just stumbled onto a rather satisfying, if obvious, extension of this work. First lets review the properties of a friend function. Recall that a friend:
- …can access the private members of a class
- …is not passed a
thispointer - …does not reside in the class’s namespace
Now consider a function which:
- …cannot access the private members of a class
- …is passed a
thispointer - …does reside in the class’s namespace
With tongue-in-cheek, I call this an enemy function (alternate names are welcome).
We can add as many enemy functions as we like to a class without reducing its degree of encapsulation, because these functions only have access to the class’s public interface. Better still, since we pass a this pointer, we don’t have the inconsistency between w.eat(.564) and nap(w) (from the Meyers article). This means we can fix std::string without breaking existing code. I envision something like this:
class Foo
{
public:
// ...
private:
// ...
enemy:
void func(); // may not access private members
}
// …time passes…
Foo f;
f.func(); //not really a member function
Update (6/4/06):
I threw caution to the wind and posted this idea to comp.lang.c++.moderated. Maxim Yegorushkin made a great suggestion: why not use a public member function in a derived class?
namespace
{
class FooBase
{
private:
int x;
public:
FooBase(int x) : x(x) {}
int size() { return x; }
};
}
class Foo : public FooBase
{
public:
Foo(int x) : FooBase(x) {}
bool empty() { return size() == 0; }
};
This does exactly what I wanted. The only downside is that I must write forwarding functions for FooBase’s constructors. I find this much more satisfying than Scott Meyer’s class Foo / namespace FooStuff method.
Update (6/15/06):
Many who object to the subclass solution do so on the grounds that it is “misuse of inheritance”. To appease them, I’ve modified the code above to wrap FooBase in an anonymous namespace. The “world” will have no idea I misused inheritance.

Latest Comments
RSS