关于c ++:带有受保护派生成员的CRTP

CRTP with Protected Derived Member

在crtp模式中,如果我们希望将派生类中的实现函数保持为受保护的,就会遇到问题。我们必须将基类声明为派生类的朋友,或者使用类似的方法(我没有在链接的项目上尝试过该方法)。是否有其他(简单)方法允许将派生类中的实现函数保持为受保护的?

编辑:下面是一个简单的代码示例:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
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;
}

上面的代码给出了使用g++4.5.1的error: ‘void D::foo()’ is protected,但如果protectedpublic替换,则编译。


这根本不是问题,在派生类中用一行来解决:

friend class Base< Derived >;

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
#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 );
}

正如Lapk推荐的那样,问题可以通过简单的friend类声明来解决:

1
2
3
4
5
6
class D:  public C<D> {
    friend class C<D>;      // friend class declaration
protected:
    void foo() {
    }
};

但是,它公开派生类的所有受保护/私有成员,并要求为每个派生类声明使用自定义代码。

以下解决方案基于链接的文章:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
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:如果您不相信编译器会优化掉这些引用,您可以用下面的#define替换derived()函数(使用MSVC 2013减少20%的反汇编代码行):

1
2
3
4
    int base_bar() { return Accessor::base_bar(_instance_ref); }

    private:
    #define _instance_ref *static_cast<D*>(this)   //D& derived() { return *(D*)this; }