Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

CRTP with Protected Derived Member

Tags:

c++

crtp

In the CRTP pattern, we run into problems if we want to keep the implementation function in the derived class as protected. We must either declare the base class as a friend of the derived class or use something like this (I have not tried the method on the linked article). Is there some other (simple) way that allows keeping the implementation function in the derived class as protected?

Edit: Here is a simple code example:

template<class D>  class C { public:     void base_foo()     {         static_cast<D*>(this)->foo();     } };   class D:  public C<D> { protected: //ERROR!     void foo() {     }    };  int main() {     D d;     d.base_foo();     return 0; } 

The above code gives error: ‘void D::foo()’ is protected with g++ 4.5.1 but compiles if protected is replaced by public.

like image 689
amit kumar Avatar asked Dec 15 '11 16:12

amit kumar


2 Answers

It's not a problem at all and is solved with one line in derived class:

friend class Base< Derived >;

#include <iostream>  template< typename PDerived > class TBase {  public:   void Foo( void )   {    static_cast< PDerived* > ( this )->Bar();   } };  class TDerived : public TBase< TDerived > {   friend class TBase< TDerived > ;  protected:   void Bar( void )   {    std::cout << "in Bar" << std::endl;   } };  int main( void ) {  TDerived lD;   lD.Foo();   return ( 0 ); } 
like image 83
lapk Avatar answered Oct 08 '22 17:10

lapk


As lapk recommended, problem can be solved with simple friend class declaration:

class D:  public C<D> {     friend class C<D>;      // friend class declaration protected:     void foo() {     } }; 

However, that exposes all protected/private members of derived class and requires custom code for each derived class declaration.

The following solution is based on the linked article:

template<class D> class C { public:     void base_foo() { Accessor::base_foo(derived()); }     int base_bar()  { return Accessor::base_bar(derived()); }  private:     D& derived() { return *(D*)this; }      // accessor functions for protected functions in derived class     struct Accessor : D     {         static void base_foo(D& derived) {             void (D::*fn)() = &Accessor::foo;             (derived.*fn)();         }         static int base_bar(D& derived) {             int (D::*fn)() = &Accessor::bar;             return (derived.*fn)();         }     }; };  class D : public C<D> { protected: // Success!     void foo() {}     int bar() { return 42; } };  int main(int argc, char *argv[]) {     D d;     d.base_foo();     int n = d.base_bar();     return 0; } 

PS: If you don't trust your compiler to optimize away the references, you can replace the derived() function with the following #define (resulted in 20% fewer lines of disassembly code using MSVC 2013):

    int base_bar() { return Accessor::base_bar(_instance_ref); }      private:     #define _instance_ref *static_cast<D*>(this)   //D& derived() { return *(D*)this; } 
like image 33
pixelgrease Avatar answered Oct 08 '22 17:10

pixelgrease