问题描述
考虑以下代码:
#include <functional>
class A{
public:
std::function<void(A* obj)> todo;
void doWork(){
if(todo)
todo(this);
}
private:
void dummy(){}
friend void todo(); // not working
};
int main(int argc,char *argv[])
{
A tmp;
tmp.todo = [](A *obj){
obj->dummy();
};
tmp.doWork();
return 0;
}
当然,由于'A::dummy': cannot access private member declared in class 'A'
,我们无法构建此代码。
我认为这是不可能的,但是有一种方法可以从lambda声明中访问A类的私有成员?
解决方法
friend void todo(); // not working
是的,它不起作用。这声明了朋友功能是一个称为“ todo”的功能。
这与具有相同名称的类成员是不同的。朋友是函数(或类),而不是类成员。
您在这里面临的基本问题是两个基本C ++方面的组合:
-
std::function<something>
是一个具体的离散类。 -
每个lambda是一个匿名类,所有lambda是离散的,不同的匿名类。
您可以声明以下内容:
friend class std::function<void(A* obj)>;
但这并不会完成任何富有成效的工作。这将允许std::function
模板本身访问此类的私有成员。因此,如果您的C ++库的std::function
模板的内部实现中的某些内容需要访问私有类成员,则现在可以执行此操作。但是,当然,std::function
本身对您的班级没有任何了解。
并且由于每个lambda本身都是离散的匿名类,因此这对任何lambda均无效。 std::function
本身是其自身的一个具体类,对于这些匿名lambda而言,它会影响类型擦除。
简而言之,这无法在C ++中完成。您真正想要拥有的是使特定的匿名lambda类成为该类的friend
。但是C ++中没有这样的语法。您必须想出其他一些替代策略,使您的lambda可以访问班级的私人成员。
好吧,您不能直接这样做。即使您成为std::function
类的朋友,它也不会成为与std::function
不同的具有自己类型的lambda本身的朋友。
我要做的是将可以访问选定私有部分的东西传递给lambda。这个习惯用法的好处是您不会暴露比您需要更多的东西:
class A {
struct B {
explicit B(A* s) noexcept : self{s} {}
void dummy();
private:
A* self;
};
void dummy();
public:
std::function<void(B)> todo;
void doWork() {
if (todo) todo(B{this});
}
};
因此,您绕过了B
,它可以访问所有内容,但仅公开dummy
。在我的示例中,该类是私有的,但是您可以将其设为公开,但这是可选的。
然后您可以像这样实现todo函数:
A tmp;
// auto will take the B type,which cannot be named here since it's private.
// Notice we pass by value,since the wrapper contains the pointer.
// Bonus: operator dot works.
// If B would be public,then you could put A::B instead of auto
tmp.todo = [](auto a) {
a.dummy(); // calls dummy,yay!
// a.doWork(); // cannot call do work,not exposed by the wrapper.
};