21

有这样的课:

class A {
public:
    bool hasGrandChild() const;

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

为什么不能hasChild()在这样的方法中定义的 lambda 表达式中使用私有方法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

当 lambda 尝试使用完全限定名称从父类访问受保护的成员时,这似乎只是一个特殊情况下的 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:

lambda 表达式的类型(也是闭包对象的类型)是唯一的、未命名的非联合类类型——称为闭包类型——其属性如下所述。此类类型不是聚合 (8.5.1)。闭包类型在包含相应 lambda-expression 的最小块作用域、类作用域或命名空间作用域中声明

然后是 C++11、11.7/1:

嵌套类是成员,因此具有与任何其他成员相同的访问权限。

所以提到的函数局部 lambda 应该与类的任何其他成员具有相同的访问权限。因此它应该能够从父类调用受保护的方法。

于 2012-08-23T07:38:02.560 回答
9

标准(C++11,§5.1.2/3)指出

lambda 表达式的类型(也是闭包对象的类型)是唯一的、未命名的非联合类类型——称为闭包类型

由于它是一个独特的类类型,它不是friendof A,因此它无权访问A的私有成员。

编译器在这里所​​做的是创建一个类类型,该类类型具有适当的成员来存储任何捕获的变量、适当的operator()等——如果你想在 C++03 中模拟 lambda,这正是你自己编写的。这种类型肯定无法访问private成员,这可能更容易想象为什么存在限制以及为什么没有解决方法

有关可能的解决方法的更新:

最好说“没有使用 lambda 的变通方法”,因为通常变通方法确实存在,尽管它们要求您放弃方便的 lambda 语法。例如,您可以:

  1. 编写一个本地类类型,显式捕获this它需要的任何其他本地变量(受 Björn Pollex 下面的评论启发)。
  2. 编写一个private方法而不是 lambda 并将其作为回调传递(例如,std::bind为了方便使用)。如果您想捕获本地人,this您可以std::bind在呼叫站点使用更多来执行此操作。
于 2012-08-13T12:10:05.710 回答
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 回答
3

您可以显式捕获this并使其成为可以访问私有成员的“成员 lambda”。

例如,考虑以下示例:

#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 回答
0

这是不可能的,因为 lambda 不是该类的一部分。这与创建类外函数并调用它而不是创建 lambda 相同。当然,它无法访问私人成员。

于 2012-08-13T12:07:57.697 回答