Search code examples
c++access-modifierspimpl-idiom

PIMPL idiom accessibility issue


I've implemented my class, lets say class A using standard PIMPL idiom.
Problem arises when i try overloading << operator for my implementation class AImpl

/* A.h */
class A {
public:
     ...
private:
     class AImpl;
     AImpl *impl;
}
/* Aimpl.h */
class AImpl {
     ...
     friend ostream &operator <<(ostream &os, const AImpl &impl);
     ...
}
/* elsewhere.cpp */
ostream &operator <<(ostream &os, const AImpl &impl) {
     ...
}

Problem stems from overloaded operator not having access to AImpl class, declared private in A.
Now i'm in sort of a dilemma over how i should resolve this. One way is declaring overloaded operator friend of class A too. The other way is making private declaration of class AImpl public.

Which approach is better and safer?


Solution

  • IMHO you're misusing the PIMPL idiom. The idiom calls for the implementation to be really private, that is AImpl should not be defined in a header file (for everyone to see), rather it should probably be defined in A.cpp where also the << operator belongs.

    If you do that the << operator is pointless to declare in header file as well, the only way you would access the PIMPL would be via the containing class anyway. You would define ostream &operator <<(ostream &os, const A &obj) instead and make that a friend of A.

    Note that with this approach the AImpl does not need to be as restrictive about access. It's field and size would only be available from A.cpp anyway. But if you want to make the internals of AImpl be private you could make ostream &operator <<(ostream &os, const A &obj) a fried of AImpl as well.

    /* A.h */
    class A {
    public:
         ...
    private:
         class AImpl;
         AImpl *impl;
    
         friend ostream &operator <<(ostream &os, const A &obj);
    }
    
    /* A.cpp */
    class AImpl {
    public:
         // OR:
         friend ostream &operator <<(ostream &os, const A &obj);
    
         ...
    }
    
    ostream &operator <<(ostream &os, const A &obj) {
         AImpl* impl = obj.impl;
         ...
    }