6

テンプレート ポリシー クラスからの保護されたメンバーは、クラス階層が正しいと思われる場合でも、アクセスできないようです。

たとえば、次のコード スニペットを使用します。

#include <iostream>
using namespace std;

template <class T>
class A {
  protected:
    T value;
    T getValue() { return value; }
  public:
    A(T value) { this->value = value; }
};

template <class T, template <class U> class A>
class B : protected A<T> {
  public:
    B() : A<T>(0) { /* Fake value */ }
    void print(A<T>& input) {
      cout << input.getValue() << endl;
    }
};

int main(int argc, char *argv[]) {
  B<int, A> b;
  A<int> a(42);
  b.print(a);
}

コンパイラ (OS X では clang ですが、gcc は同じタイプのエラーを返します) は次のエラーを返します。

Untitled.cpp:18:21: error: 'getValue' is a protected member of 'A<int>'
      cout << input.getValue() << endl;
                ^
Untitled.cpp:25:5: note: in instantiation of member function 'B<int, A>::print' requested here
  b.print(a);
    ^
Untitled.cpp:8:7: note: can only access this member on an object of type 'B<int, A>'
    T getValue() { return value; }
      ^
1 error generated.

奇妙なことに、コンパイラからの最後のメモは完全に正しいのですが、bオブジェクトの型が であるため、既に適用されています'B<int, A>'。それはコンパイラのバグですか、それともコードに問題がありますか?

ありがとう

4

4 に答える 4

10

保護されたアクセスの意味を誤解しています。

保護されたメンバーは、派生クラスによって呼び出すことができます。ただし、クラス自体に含まれる基本オブジェクトのみ。

たとえば、次を使用して問題を単純化する場合:

class A {
protected:
    void getValue(){}
};

class B : protected A
{
public:
    void print(A& input)
    {
        input.getValue(); //Invallid
    }
};

クラス自体内の「A」オブジェクト以外の「A」オブジェクトで getValue を呼び出すことはできません。たとえば、これは有効です。

    void print()
    {
        getValue(); //Valid, calling the base class getValue()
    }

ダン・ニッセンバウムとシャクロフが指摘したように。ただし、これも有効です。

void print(B& input)
{
    input.getValue();
}

これは、input が B のオブジェクトであると明示的に述べているためです。コンパイラは、B のすべてのオブジェクトが getValue へのアクセスを保護していることを認識しています。A& を渡す場合、オブジェクトは C の型である可能性もあり、プライベート アクセスで A から派生する可能性があります。

于 2013-03-29T11:46:14.980 に答える
6

テンプレートのことは少し忘れて、これを見てみましょう。

 class A {
   protected:
     int value;
     int getValue() { return value; }
   public:
     A(int value) { this->value = value; }
 };

 class B : protected A {
   public:
     B() : A(0) { /* Fake value */ }
     void print(A& input) {
       cout << input.getValue() << endl;
     }
 };

insideの非公開メンバーにアクセスできないため、print()メソッドの実装が間違っていますその理由は次のとおりです。 内からは、 の非パブリック メンバーにのみアクセスできます。これらのメンバーは、継承されているかどうかに関係なく、継承されている可能性があります。ABBB

一方、A& inputは のインスタンスへの参照ではない場合がありますB。別のサブクラスへの参照である可能性があります (getValue()アクセスできない可能性があります)。

于 2013-03-29T11:55:10.323 に答える
2

テンプレートに惑わされないでください。エラーとは関係ありません。mainコンパイラが不平を言っている行は、タイプのオブジェクトを作成しB<int, a>、保護されたメンバーにアクセスしようとします。タイプに関係なく、それは合法ではありません。メンバー関数またはフレンド関数からのみ保護されたメンバーを使用できます。例えば:

struct S {
protected:
    void f();
};

int main() {
    S s;
    s.f(); // error: attempts to call a protected member function
}
于 2013-03-29T11:40:48.483 に答える
2

派生クラスのメンバー関数は、引数として渡されたオブジェクトの明示的に宣言されたクラスが派生クラス(またはそれ以上の派生クラス)。

基本クラスの型として明示的に渡されたオブジェクトは、派生クラスのメンバー関数内でアクセスされる保護されたメンバーを持つことはできません。

つまり、次の場合:

class A
{
protected:
    int x;
}

class B : public A
{
    void foo(B b)
    {
        b.x; // allowed because 'b' is explicitly declared as an object of class B
    }
    void goo(A a)
    {
        a.x; // error because 'a' is explicitly declared as having *base* class type
    }
};

...その後、a.x引数の明示的な型が であるため、行は許可されませんがA、保護されたアクセスの規則は、メンバーにアクセスしようとしているクラスと同じクラスとして明示的に定義されたオブジェクトにのみ適用されます。(...またはそれから派生したクラス。つまり、から派生しclass CB場合、クラスのオブジェクトとして明示的に宣言されたオブジェクトを渡すと、メンバー関数内でCxアクセスできます。)B

この理由は、シャクロフが書いたときに与えられています(言い換え)

A& 入力は、B のインスタンスへの参照ではない可能性があります。別のサブクラスへの参照である可能性があります (getValue() にアクセスできない可能性があります)。

この答えの優れた説明もここに示されています: access a protected member of a base class in another subclass .

興味深いことに、これは次の C++ 標準から来ていると思います。

11.4 保護されたメンバー アクセス [class.protected] 1 非静的データ メンバーまたは非静的メンバー関数がその命名クラス (11.2) の保護されたメンバーである場合、11 節で前述したものを超える追加のアクセス チェックが適用されます。以前は、参照が一部のクラス C のフレンドまたはメンバーで発生するため、保護されたメンバーへのアクセスが許可されます。アクセスがメンバーへのポインターを形成することである場合 (5.3.1)、nested-name-specifier は C またはC から派生したクラス。他のすべてのアクセスには、(おそらく暗黙的な) オブジェクト式が含まれます (5.2.5)。この場合、オブジェクト式のクラスは C または C から派生したクラスでなければなりません。

于 2013-03-29T13:05:47.943 に答える