18

ブログ投稿からプライベートメンバーへのアクセス: Johannes Schaubによるより安全な不快感-litb

template<typename Tag, typename Tag::type M>
struct Rob { 
  friend typename Tag::type get(Tag) {
    return M;
  }
};

// use
struct A {
  A(int a):a(a) { }
private:
  int a;
};

// tag used to access A::a
struct A_f { 
  typedef int A::*type;
  friend type get(A_f);
};

template struct Rob<A_f, &A::a>;

int main() {
  A a(42);
  std::cout << "proof: " << a.*get(A_f()) << std::endl;
}

内部で定義されていないため、オブジェクトからget関数を呼び出すにはどうすればよいですか?aclass A

編集:

a.*get<A_f>() getが=>okの代わりにパラメータとしてTagを持たなければならない理由がわかりません。これは ADLメカニズムによるものです。

4

3 に答える 3

8

あなたはgetから電話をかけていませんa!実際、returnを取得するのは、内部のメンバーへのクラスポインターであり、Aそのタイプは、その値にアクセスするint A::*ためのインスタンスが必要です。A

たとえば、コードを少し試してみましょう。

struct A {
    A(int a):a(a) { }
    int b;
private:
    int a;
};
void test() {
    auto p = &A::b;
    std::cout << a.*p << std::endl;
}

p中から電話しましたaか?aはありませんp、これはまさにあなたのコードで起こったことであり、get関数は戻り&A::a、あなたはその値を読み取るために使用aします!それだけです。何も問題はなく、すべてのコンパイラでコンパイルされると思います。

ここでのもう1つの質問は、C++がのプライベートメンバーを使用してテンプレートを宣言できる理由ですA。C ++標準は言う:

14.7.2p8通常のアクセスチェックルールは、明示的なインスタンス化を指定するために使用される名前には適用されません。[注:特に、関数宣言子で使用されるテンプレート引数と名前(パラメーター型、戻り型、例外仕様を含む)は、通常はアクセスできないプライベート型またはオブジェクトである可能性があり、テンプレートはメンバーテンプレートまたはメンバー関数である可能性があります通常はアクセスできません。]

typedefただし、テンプレートをインスタンス化または指定しようとすると、エラーが発生します。例を少し変更してみましょう。

struct A {
private:
    int a;
    friend void f();
};

// Explicit instantiation - OK, no access checks
template struct Rob<A_f, &A::a>;

// Try to use the type in some way - get an error.
struct Rob<A_f, &A::a> r;            // error
typedef struct Rob<A_f, &A::a> R;    // error
void g(struct Rob<A_f, &A::a>);      // error

// However, it's Ok inside a friend function.
void f() {
    Rob<A_f, &A::a> r;               // OK
    typedef Rob<A_f, &A::a> R;       // OK
}
于 2012-10-20T23:50:49.283 に答える
1

これはgccの既知のコンパイラバグであり、後のリリースで修正されました。見る-:

http://gcc.gnu.org/bugzilla/show_bug.cgi?id=41437

于 2012-10-21T04:08:33.820 に答える
1

クラス内に実装した場合でも、フレンド関数は常にグローバルスコープにあるため、これは合法です。言い換えれば、これは:

class A
{
    friend void go() {}
};

次のショートカットです。

class A
{
    friend void go();
};

void go() {}
于 2012-10-20T22:40:51.237 に答える