std ::函数作为班级的朋友

问题描述

考虑以下代码

#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 ++方面的组合:

  1. std::function<something>是一个具体的离散类。

  2. 每个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.
};