Search code examples
c++polymorphismhierarchyfriendcrtp

Why doesn't the following CRTP hierarchy compile?


I am trying to implement a CRTP hierarchy of classes. I am interested in the base class to have access to the data member of a derived class down the chain:

#include <iostream>

template <class Derived>
class A {
public:
    void showv() {
        std::cout << static_cast<const Derived*>(this)->v << std::endl;
    }
};

template <class Derived>
class B : public A< Derived > {
    typedef A<Derived> base;
    friend base;
};

class fromA : public A<fromA> {
    typedef A<fromA> base;
    friend base;
protected:
    int v = 1;
};

class fromB : public B<fromB>
{
    typedef B<fromB> base;
    friend base;
protected:
    int v = 2;
};

int main()
{
    // This runs ok
    fromA derived_from_a;
    derived_from_a.showv();

    // Why doesn't the following compile and complains about the protected member?
    fromB derived_from_b;
    derived_from_b.showv();

    return 0;
}

Demo

Although the first derived class (fromA) compiles and runs as expected, the second (fromB), that derives from a class derived fromA, doesn't.

  1. What is the reason the friend declarations are not channeled through?
  2. Any suggestions on a workaround?

Solution

  • The issue is: the friend of my friend is not my friend.

    In fromA you have

    typedef A<fromA> base;
    friend base;
    

    Which makes A<fromA> a friend and show can access fromA's protected member.

    In fromB you also have

    typedef B<fromB> base;
    friend base;
    

    But this doesn't make A a friend, it makes B your friend. Even though A is a friend of B it does not mean it is also now a friend of fromB and this is why you cannot access v in show.

    One way you can fix this is to make typedef A<Derived> base; public or protected in B and then in fromB you can add friend base::base; which would give A access.