21

このようなクラスを持つ:

class A {
public:
    bool hasGrandChild() const;

private:
    bool hasChild() const;
    vector<A> children_;
};

hasChild()このようなメソッドで定義されたラムダ式でプライベート メソッドを使用できないのはなぜhasGrandChild()ですか?

bool A::hasGrandChild() const {
    return any_of(children_.begin(), children_.end(), [](A const &a) {
        return a.hasChild();
    });
}

コンパイラは、メソッドhasChild()がコンテキスト内でプライベートであるというエラーを発行します。回避策はありますか?

編集: 私が投稿したコードは元々機能しているようです。同等だと思っていたのですが、GCCで動かないコードは以下のようなものです。

#include <vector>
#include <algorithm>

class Foo;

class BaseA {
protected:
    bool hasChild() const { return !children_.empty(); }
    std::vector<Foo> children_;
};

class BaseB {
protected:
    bool hasChild() const { return false; }
};

class Foo : public BaseA, public BaseB {
public:
  bool hasGrandChild() const {
    return std::any_of(children_.begin(), children_.end(), [](Foo const &foo) {
        return foo.BaseA::hasChild();
      });
  }  
};

int main()
{
  Foo foo;
  foo.hasGrandChild();
  return 0;
}

これは機能しないため、完全修飾名に問題があるようですが、これは機能します。

4

5 に答える 5

30

ラムダが完全修飾名を使用して親クラスから保護されたメンバーにアクセスしようとする特別なケースでは、GCC のバグのようです。これは機能しません:

class Base {
protected:
    bool hasChild() const { return !childs_.empty(); }
    std::vector<Foo> childs_;
};

class Foo : public Base {
public:
  bool hasGrandChild() const {
    return std::any_of(childs_.begin(), childs_.end(), [](Foo const &foo) {
      return foo.Base::hasChild();
    });
  }  
};

、しかしこれはうまくいきます:

class Foo : public Base {
public:
  bool hasGrandChild() const {
    return std::any_of(childs_.begin(), childs_.end(), [](Foo const &foo) {
      return foo.hasChild();
    });
  }  
};

C++11、5.1.2/3 によると:

ラムダ式の型 (クロージャー オブジェクトの型でもあります) は、名前のない一意の非共用体クラス型 — クロージャー型と呼ばれます — のプロパティについては、以下で説明します。このクラス タイプは集合体ではありません (8.5.1)。クロージャー型は、対応する lambda-expression を含む最小のブロック スコープ、クラス スコープ、または名前空間スコープで宣言されます

そして、C++11、11.7/1:

ネストされたクラスはメンバーであるため、他のメンバーと同じアクセス権を持ちます。

したがって、前述の関数ローカル ラムダには、クラスの他のメンバーと同じアクセス権が必要です。したがって、親クラスから保護されたメソッドを呼び出すことができるはずです。

于 2012-08-23T07:38:02.560 に答える
9

標準 (C++11、§5.1.2/3) は次のように述べています。

ラムダ式の型 (クロージャ オブジェクトの型でもあります) は、名前のない一意の非共用体クラス型であり、クロージャ型と呼ばれます。

friendこれはofではない一意のクラス型であるため、のプライベート メンバーAにはアクセスできません。A

ここでコンパイラが行うことは、キャプチャされた変数を格納するための適切なメンバーを持つクラス型を作成するoperator()ことです。これは、C++03 でラムダをエミュレートする場合に自分で作成するものとまったく同じです。この型はメンバーにアクセスできません。これにより、制限が存在する理由と回避策がない理由privateを簡単に視覚化できます。

考えられる回避策に関する更新:

「ラムダを使用した回避策はありません」と言ったほうがよいでしょう。なぜなら、便利なラムダ構文を使用する必要はありますが、一般的に回避策は存在するからです。たとえば、次のことができます。

  1. 必要な他のローカルと一緒に明示的にキャプチャするローカル クラス タイプを記述thisします (以下の Björn Pollex のコメントに触発されました)。
  2. privateラムダの代わりにメソッドを記述し、それをコールバックとして渡します (たとえば、便宜上使用しstd::bindます)。さらにローカルをキャプチャしたい場合は、呼び出しサイトでthisさらに使用できます。std::bind
于 2012-08-13T12:10:05.710 に答える
3

明示的にキャプチャthisして、プライベートメンバーにアクセスできる「メンバーラムダ」にすることができます。

たとえば、次のサンプルについて考えてみます。

#include <iostream>
class A {
private:
    void f() { std::cout << "Private"; }
public:
    void g() { 
        [this] { 
            f(); 
            // doesn't need qualification 
        }(); 
    }
};
class B {
private:
    void f() { std::cout << "Private"; }
public:
    void g() { [] { f(); }(); } // compiler error
};
int main() {
    A a;
    a.g();
}
于 2012-08-24T21:58:47.123 に答える
3

回避策:

typedef  bool (A::*MemFn)(void) const;

bool A::hasGrandChild() const {
    MemFn f = &A::hasChild;
    return any_of(childs_.begin(), childs_.end(), [=](A const &a) {
            return (a.*f)();
    });
}
于 2012-08-13T12:41:57.473 に答える
0

ラムダはクラスの一部ではないため、これは不可能です。クラス外の関数を作成し、ラムダを作成する代わりにそれを呼び出すのと同じです。もちろん、プライベートメンバーにはアクセスできません。

于 2012-08-13T12:07:57.697 に答える