6

まず、C++ 標準 (ISO/IEC 14882:2003): Section 11.5, Paragraph 1について知っていますが、これはそうではありません (ただし、コンパイラは明らかにそうではないと考えています)。

このポインターを介して派生クラス メソッドで保護された基本クラス メソッドを呼び出そうとし、基本クラスポインターに静的キャストされ、MSVC2008エラー C2248: 'A::f' : cannot access protected member defined 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 のフレンドまたはメンバーで発生するため、許可されます。オブジェクト式 (5.2.5)。この場合、オブジェクト式のクラスは C または C から派生したクラスでなければなりません。

したがって、基本クラスへのキャストを介した保護されたメンバー アクセスは、Bこの許可に違反するため、許可されません。上記のように使えるという理由でも不要ですthis->B::f()


実際の CRTP の動機の場合、は の基本クラスではないため(継承関係は逆方向です)、なしf()で呼び出すことはできません。は の基本クラスではないため、いずれにしてもそのメソッドを呼び出すことはできません。簡単な回避策の 1 つは、ポインターでを使用することです。static_castDB<D>DB<D>protectedB<D>friend B<D>Dstatic_castthis

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

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

気になる部分Bへのアクセスを許可する場合は、その部分を別の基本クラスに移動して、CRTP メカニズムを で分離できます。privateDprivateD

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非常に奇妙です。b1b2

あなたの段落11.5を引用する:

[...]アクセスは、派生クラス自体へのポインター、参照、またはオブジェクトを介して行う必要があります。

ただし、オブジェクト自体が同じであっても、static_cast<B*>(this)タイプB*は ではなくです。D*実際には、ポインタの値はこの問題とは無関係であり、式の型だけです:

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

しかし、一度適用すると、コンパイラは B から派生したクラスの内部にいることをどのように知るのでしょうstatic_castthis? 私の(謙虚な)意見では、オブジェクトを作成する場合、カプセル化に違反したくないため、オブジェクトでプライベートまたは保護されたメソッドBを呼び出すことは許可されないことを期待しています。オブジェクトがクラス メソッドの外にある限り、オブジェクトがどこで作成されるかは問題ではありません。BBBB

于 2012-08-03T12:08:32.637 に答える