6

The following code doesn't work, as you can't static_cast from private base class.

Replacing the cast with a C-style cast works (although I originally thought this would invoke undefined behaviour apparently it does not, see this answer), but is rather ugly as it also allows you to bypass const-checking, etc. The other approach would be to make CRTPBase a friend, but that would expose all of Derived's private members.

Is there another way of writing this without using a C-style cast and without making CRTPBase a friend?

template<typename T>
struct CRTPBase {
    void callBase() {
        T * derived = static_cast<T*>(this);
        derived->publicMethod();
    }
};

struct Derived : private CRTPBase<Derived> {
    void callParent() { this->callBase(); }
    void publicMethod() {}
};

int main() {
    Derived d;
    d.callParent();
    return 0;
}
Community
  • 1
  • 1
jleahy
  • 16,149
  • 6
  • 47
  • 66
  • I've noticed this situation several times now, and didn't find a different solution than require `public` final implementations. Though a `private` _primary base_ declaration of a method can be made, if you have more than one inheritance level. – πάντα ῥεῖ Apr 21 '14 at 16:32
  • You know just adding `using CRTPBase::callBase;` in the public derived part makes it public in `Derived`? – Deduplicator Apr 21 '14 at 16:35
  • +1 for an interesting fact about C-style casts. It makes sense for `static_cast` to restrict casting *to* a private base, but disallowing casting *from* a private base is a rather more obscure restriction. – Kerrek SB Apr 21 '14 at 16:37
  • @Deduplicator Yes, but that then exposes it to everybody using Derived. The point of using private inheritance is to hide that helper method. Making it a friend works, I mentioned that in my question, but that also has undesirable side-effects (exposing all your private members). – jleahy Apr 21 '14 at 16:38
  • @Deduplicator _'Did you try adding ...'_ Didn't the OP asked for doing better? – πάντα ῥεῖ Apr 21 '14 at 16:39
  • @KerrekSB _'... but that would be obscene ...'_ Should I blush now, because I already have used such solutions :O ... – πάντα ῥεῖ Apr 21 '14 at 16:40
  • You could make only the relevant function a friend. Though I somehow doubt that's restricted enough for you. – Deduplicator Apr 21 '14 at 16:43
  • 2
    @πάνταῥεῖ: It's just way more roundabout than it should be: `template void callBase(T * p) { (p->*FP)(); }`, call with `callBase<&Derived::publicMethod>(this)`. Probably optimizable so that no actual arguments get passed. – Kerrek SB Apr 21 '14 at 16:44
  • @jleahy: For friendship you only need `friend void CRTPBase::callBase();`, which doesn't expose any more than necessary. – Kerrek SB Apr 21 '14 at 16:47
  • @Kerrek: When you go for a template, why restrict the exact callable which can be passed? – Deduplicator Apr 21 '14 at 16:47
  • @KerrekSB That reads sound. I had the additional problem to bind to a plain c-API, thus I finally ended up using plain (static) function pointers. I have added a meta-level to bind against _'normal'_ class member functions. – πάντα ῥεῖ Apr 21 '14 at 16:48
  • btw, code works in VC2013 a VC2010 – relaxxx Apr 21 '14 at 17:04
  • 1
    is there any reason why you don't make inheritance public and the callback protected? it seems to me the obvious choice. – Alexander Oh Apr 21 '14 at 17:20
  • 1
    @Alex That's actually the best solution in this case, and it actually applies to my original problem also, not sure why I didn't consider that. – jleahy Apr 21 '14 at 19:28
  • 1
    @Alex: You should definitely post that as an answer. – Kerrek SB Apr 21 '14 at 22:18

2 Answers2

2

Not an ideal solution, but you can restrict the friendship to a unique method as follow:

template<typename T>
struct CRTPBase {
    friend T; // So T can see asDerived.
    void callBase() { asDerived()->publicMethod(); }
private:
    T* asDerived() { return static_cast<T*>(this); }
};

struct Derived : private CRTPBase<Derived> {
    friend Derived* CRTPBase<Derived>::asDerived();
    void callParent() { this->callBase(); }
    void publicMethod() {}
};
Jarod42
  • 203,559
  • 14
  • 181
  • 302
2

I think the best solution is to avoid private inheritance and instead opt for data hiding. Marking the member function protected will prevent access from everywhere except derived classes. A further bonus public inheritance is used instead.

template<typename T>
class CRTPBase {
protected:
  void callBase() {
    T * derived = static_cast<T*>(this);
    derived->publicMethod();
  }
};

struct Derived : public CRTPBase<Derived> {
  void callParent() { this->callBase(); }
  void publicMethod() {}
};

int main() {
  Derived d;
  d.callParent();
  d.callBase() // <- illegal
  return 0;
 }
Alexander Oh
  • 24,223
  • 14
  • 73
  • 76