c++ - 从模板参数派生的类没有 protected 成员访问

标签 c++ templates inheritance

从模板参数派生类时,派生类没有 protected 成员访问权限,即使它从 std::is_base_of 返回 true 也是如此

例如:

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

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

和模板类

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;
    };

如果我们然后实例化并调用

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()
}

将 BaseClass 指针转换为 C 类型的指针允许我们正确调用该函数,因为我们可以调用 protected 成员函数,并且可以正确调用覆盖。

这种事情不会发生在类 B 上,它可以直接调用 A::Do(),因为它是从 A 派生的。

C 派生自 BaseClass,但无权访问 protected 成员函数。 指向 BaseClass 的指针就像一个外部指针,不被模板类调用。

因此,总体问题是:

  • 这是合法/安全/良好的代码吗?
  • 有什么原因吗?
  • 有更好的方法吗?

编辑更多细节和推理:

在这种情况下,C 是一个包装类,允许我转发对 B 实例的函数调用,同时向其添加功能。因此需要存储 B 的实例,因为它包含将被更新的变量,而 B 是我存储或派生并进一步专门化的具体实例。

如果我们有

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

然后我们就这样创建了

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

我希望当我调用 c.Do() 时,它会调用 D 特化,并且在那个特定实例上。

无论如何,我似乎确实需要让 C 成为 BaseClass 的友元,如下所示:

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

class B
{
    ...

    friend class C<B>;
};

最佳答案

这对于 m_Base 是非法的类型 A* .

m_Base->Do();

除非所述对象为 *this,否则您无法访问对象的 protected 成员.这是一个巨大的简化,但它在 99% 的时间都有效。

作为解决方案,您可以制作 template<class T> class C一个friendA :

//define C beforehand

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

最后,请注意

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

是非法的:c作为BaseClass (在您的使用中,从 &b 实例化)您不能将其转换为 C<BaseClass>* .

关于c++ - 从模板参数派生的类没有 protected 成员访问,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/56079961/

相关文章:

c++ - 新手 - c++ 中的矩阵加法实现

c++ - 具有自定义数据结构的 C++ 中的运算符<<

node.js - 如何将一个模板的一些数据发送到另一个模板 Meteor

java - 子类化泛型 - 构造函数继承

c++ - 从程序集中访问类方法

c++ - 使用两个 cpp 文件时使用未声明的标识符

c++ - 不能使用指针作为默认模板参数

c++ - 使用 `std::conditional_t` 根据其模板参数定义类的 `typedef`

c# - 抽象基类或辅助类

python - 如果子类由构造逻辑确定,那么分离类及其父类逻辑的最pythonic和优雅的方法是什么?