通过此指针调用受保护的基类方法,该指针在派生类(C++)中转换为基类

Yur*_*ash 6 c++ inheritance compiler-errors protected

首先,我了解C++标准(ISO/IEC 14882:2003):第11.5节,第1段,这不是那种情况(但是compliler显然不这么认为).

我尝试通过这个指针在派生类方法中调用受保护的基类方法,静态转换为基类指针并在MSVC2008中出现错误C2248:'A :: f':无法访问在类'A'中声明的受保护成员.

必须在"奇怪的重复模板模式"的上下文中执行此操作,但我可以在更简单的代码中重现此错误,如下所示:

class B
{
protected:
    void f(){}
};

class D : public B
{
public:
    void g()
    {
        f(); // ok
        this->f(); // ok
        static_cast<B*>(this)->f(); // C2248 in MSVC2008
        dynamic_cast<B*>(this)->f(); // C2248
        ((B*)this)->f(); // C2248
    }
};
D d; d.g();
Run Code Online (Sandbox Code Playgroud)

似乎编译器认为将此指针作为指向其他实例的指针,是吗?

在这种情况下编译器是错误的,你怎么看?


好的,我的真实代码更像是:

template<class T>
class B
{
public:
    void g()
    {
        f(); // error C3861: 'f': identifier not found
        this->f(); // error C3861: 'f': identifier not found

        // static_cast to derived class
        static_cast<T*>(this)->f(); // C2248 in MSVC2008
    }
};

class D : public B<D>
{
protected:
    void f(){}
};
Run Code Online (Sandbox Code Playgroud)

我把它转换派生类,我不能使用this-> f();


顺便说一下,我看到这段代码对于使用不安全,例如class E : public B<D> {...};:compilable,但static_cast会导致错误的强制转换.

eca*_*mur 13

编译器是正确的.要显式访问B::f成员函数,您可以编写:

this->B::f();
Run Code Online (Sandbox Code Playgroud)

相关语言是:

11.4受保护的成员访问[class.protected]

[...]授予对受保护成员的访问权限,因为引用发生在朋友或某个C类成员中.[...]访问受保护成员[...]涉及[可能是隐含的]对象表达式(5.2.5).在这种情况下,对象表达式的类应为C或从C派生的类.

因此,通过强制转换为基类的受保护成员访问B违反了此授权,并且是不允许的.您也可以使用this->B::f()上述原因.


在你的实际CRTP动机的情况下,你是正确的,你不能f()没有a static_cast,因为D它不是一个基类B<D>(继承关系在另一个方向).由于D不是基类B<D>,因此无论如何都无法调用其protected方法B<D>.一个简单的解决方法是friend B<D>,以D和使用static_cast上的this指针:

template<typename T>
class B {
public:
    void g() {
        static_cast<T *>(this)->f();
    }
};

class D : public B<D>
{
    friend class B<D>;
    ...
Run Code Online (Sandbox Code Playgroud)

如果允许B访问您担心的private部分D,您可以将private部件移动到另一个基类并隔离CRTP机制D:

template<class T> class B {
public:
    void g() {
        static_cast<T*>(this)->f();
    }
};

class C {
private:
    void h();
protected:
    void f(){ std::cout << "D::f\n"; }
};

class D: protected C, public B<D>
{
    friend class B<D>;
};
Run Code Online (Sandbox Code Playgroud)

这里B<D>是从防止调用C::h友谊既不继承,也不传递.