简体   繁体   English

来自Template参数的派生类没有受保护的成员访问权限

[英]Derived class from Template argument doesn't have protected member access

When deriving a class from a template argument, the derived class doesn't have protected member access, even though it returns true from std::is_base_of 从模板参数派生类时,派生类没有受保护的成员访问权限,即使它从std::is_base_of返回true

eg: 例如:

class A
{
    protected:
        virtual void Do() 
        {
            std::cout << "A::Do()";
        }
};

class B : public A
{
    protected:
        virtual void Do()
        {
            std::cout << "B::Do() - "; A::Do();
        }
};

and the template class 和模板类

template <class BaseClass>
class C : public BaseClass
{
    public:
        C(BaseClass* c) :
            m_Base(c),
            m_Self(static_cast<C<BaseClass>*>(c))
        {}

        // this will never call the passed in class version
        void Do1()
        {
            BaseClass::Do();
        }

        // this has the error 'virtual int A::Do()' is protected within this context
        void Do2()
        {
            m_Base->Do();
        }

        void Do3()
        {
            m_Self->Do();
        }

        BaseClass* m_Base;
        C<BaseClass>* m_Self;
    };

if we then instantiate and call 如果我们然后实例化并调用

int main()
{
    A a;
    B b;
    C<A> c(&b);

    std::is_base_of<A, C<A> >::value; // this is true

    c.Do1(); // calls A::Do()
    c.Do2(); // throws a protected access error
    c.Do3(); // calls B::Do()
}

casting the BaseClass pointer to a pointer of type C allows us to call the function correctly, both in that we can call the protected member function, and the override is correctly called. 将BaseClass指针转换为类型C的指针允许我们正确地调用函数,因为我们可以调用受保护的成员函数,并且正确调用覆盖。

this kind of thing doesn't happen with class B, which can call A::Do() directly, as it is derived from A. 这种事情不会发生在B类,它可以直接调用A :: Do(),因为它是从A派生的。

C is derived from BaseClass, yet does not have access to the protected member functions. C派生自BaseClass,但无权访问受保护的成员函数。 the pointer to BaseClass acts like it is an external pointer, not being called by the template class. 指向BaseClass的指针就像是一个外部指针,而不是由模板类调用。

the overall question is therefore: 因此,整体问题是:

  • is this legal/safe/good code? 这是合法/安全/良好的代码吗?
  • is there a reason why? 有原因吗?
  • is there a better method to doing this? 有没有更好的方法来做到这一点?

edit for some more details and reasoning: 编辑更多细节和推理:

in this case, C is a wrapper class that allows me to forward on the function calls of an instance of B , whilst adding functionality to it. 在这种情况下, C是一个包装类,它允许我转发B实例的函数调用,同时为其添加功能。 hence the need to store the instance of B , as its contains variables that will be updated, and B is the concrete instance that i store - or derive from and further specialise. 因此需要存储B的实例,因为它包含将要更新的变量, B是我存储的具体实例 - 或者从中派生并进一步专门化。

if we had 如果我们有

class D : public B
{
    protected:
        virtual void Do()
        {
            std::cout << "D::Do()";
        }
};

and we created as thus 我们就这样创造了

D d;
C<B> c(&d)

i would expect when i call c.Do() that it calls the D specialisation, and on that particular instance. 我希望当我调用c.Do()它调用D ,并在该特定实例上。

however this goes, it does seem that i need to make C a friend of BaseClass as follows: 但是,这看起来似乎我需要让C成为BaseClass的朋友,如下所示:

// forward declared as they live separately
template <class BaseType> class C;

class B
{
    ...

    friend class C<B>;
};

This is illegal with m_Base of type A* . 对于A*类型A* m_Base ,这是非法的。

m_Base->Do();

You cannot access a protected member of an object except when the said object is *this . 除非上述对象是*this否则无法访问对象的受保护成员。 This is a huge simplification, but it works 99% of the time. 这是一个巨大的简化,但它在99%的时间都有效。

As a solution, you could make template<class T> class C a friend of A : 作为解决方案,您可以将template<class T> class C作为Afriend

//define C beforehand

class A
{
friend template<class T> class C;
protected:
    virtual Do() 
    {
        std::cout << "A::Do()";
    }
};

Finaly, note that 最后,请注意

m_Self(static_cast<C<BaseClass>*>(c))

is illegal: c being a BaseClass (in your usage, instancied from &b ) you cannot cast it to a C<BaseClass>* . 是非法的: c是一个BaseClass (在您的使用中,从&b实例化)您不能将它转换为C<BaseClass>*

There is a confusion between derivation and composition: a derived class is a base class, a composed class has a base class as a member. 派生和组合之间存在混淆:派生类基类,组合类具有基类作为成员。 Here your class is both derived and composed , c of class C<A> is A but also has a pointer to b of class B . 这里你的类是派生组合的c类的C<A>A但也有一个指向Bb的指针。

When you call c.Do1 , you call the method Do of BaseClass , which is A in your example (because of C<A> ), and you call A::Do . 当你调用c.Do1 ,你调用方法Do of BaseClass ,在你的例子中是A (因为C<A> ),你调用A::Do c.Do1

When you call c.Do2 , you call the method Do of b , which is protected! 当你调用c.Do2 ,你调用b的方法Do ,它受到保护!

When you call c.Do3 , you call the method Do of m_self , which is a pointer to b , so you call B::Do . 当你调用c.Do3 ,你调用c.Do3的方法Do ,这是一个指向b的指针,所以你调用B::Do m_self

I think you should just remove the 2 members m_Base and m_Self , and your constructor would be: 我想你应该删除2个成员m_Basem_Self ,你的构造函数将是:

C: BaseClass() {}

Then you would have: 然后你会有:

C<A> c;
c.Do();  // calls A::Do
C<B> d;
d.Do();  // calls B::Do

You can also override Do in C . 您还可以覆盖DoC

You don't need to store a pointer of BaseClass. 您不需要存储BaseClass的指针。 C can't access m_base because Do() is protected, which means is private from the outside. C无法访问m_base,因为Do()受到保护,这意味着从外部是私有的。

you can just call 你可以打电话

BaseClass::Do()

because in that context is public (you're calling a method of your parent class). 因为在该上下文中是公共的(您正在调用父类的方法)。

Except for storing pointers, your approach is used for static polymorphism and policy based design. 除了存储指针之外,您的方法还用于静态多态和基于策略的设计。

You can find more in this post: C++: Inherit class from template parameter 您可以在这篇文章中找到更多: C ++:从模板参数继承类

You would like to take a look to the Curiously recurring template pattern 您想看看奇怪的重复模板模式

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM