6

首先,我知道C++ 标准(ISO/IEC 14882:2003):第 11.5 节,第 1 段,但事实并非如此(但编译器显然不这么认为)。

我尝试通过指针在派生类方法中调用受保护的基类方法,静态转换为基类指针,并在 MSVC2008 中出现错误 C2248: 'A::f' : cannot access protected member declaration in class '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();

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

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


好的,我的真实代码更像这样:

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

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


顺便说一句,我看到这段代码不安全,例如class E : public B<D> {...};:可编译,但 static_cast 会产生错误的强制转换。

4

3 回答 3

13

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

this->B::f();

相关语言是:

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

[...] 授予对受保护成员的访问权限,因为引用发生在某个 C 类的朋友或成员中。 [...] 对受保护成员的访问 [...] 涉及 [s] a(可能是隐式的)对象表达式(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_castthis

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

class D : public B<D>
{
    friend class B<D>;
    ...

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

这里B<D>被阻止调用C::h,因为友谊既不是继承的也不是传递的。

于 2012-08-03T12:07:36.147 回答
1

我认为编译器是对的。

假设如下:

void g()
{
    B *b1 = this;
    B *b2 = GetUnrelatedB();
    b1->f(); //Error?
    b2->f(); //Error!
}

这个b1案子相当于你的static_cast,但会很奇怪,b1会被允许,b2不会。

引用您的第 11.5 段:

[...] 必须通过派生类本身的指针、引用或对象进行访问。

butstatic_cast<B*>(this)是 type B*, not D*,不管对象本身是一样的。实际上,指针的值与这个问题无关,只有表达式的类型:

void g()
{
    B *b2 = GetUnrelatedB();
    static_cast<D*>(b2)->f(); //ok!
}
于 2012-08-03T12:13:36.797 回答
0

static_cast但是,一旦你申请了,编译器怎么知道你在一个从 B 派生的类中this呢?在我的(谦虚)看来,如果我创建一个B对象,我希望不允许调用B该对象的私有或受保护方法B,因为我们不想违反封装。只要对象在类方法B之外,它在哪里创建都没有关系。B

于 2012-08-03T12:08:32.637 回答