std::function 作为 class 的朋友

std::function as a friend of the class

考虑这段代码:

#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 声明访问 class A 的私有成员??

friend void todo(); // not working

是的,它不起作用。这声明友元函数是一些名为“todo”的函数。

这与 class 同名成员不同。好友是函数(或 classes),而不是 class 成员。

您在这里面临的根本问题是两个基本 C++ 方面的组合:

  1. std::function<something> 是一个具体的、独立的 class。

  2. 每个lambda都是匿名的class,所有lambda都是离散的,不同的匿名classes.

你可以这样声明:

friend class std::function<void(A* obj)>;

但这不会取得任何成果。这将允许 std::function 模板本身访问此 class 的私有成员。因此,如果 C++ 库的 std::function 模板的内部实现中的某些内容需要访问私有 class 成员,它现在可以做到这一点。但是,当然,std::function 本身没有任何东西知道你的 class。

并且由于每个 lambda 本身都是离散的匿名 class,这对任何 lambda 都没有影响。 std::function 本身就是它自己的具体 class,它会影响这些匿名 lambda 的类型擦除。

简而言之,这在C++中是做不到的。你真正想要的是创建一个特定的匿名 lambda class a friend 这个 class。但是C++中没有这样的语法。您必须想出一些其他替代策略,让您的 lambda 表达式可以访问您的 class 的私有成员。

嗯,你不能直接这样做。即使您将 std::function class 加好友,它也不会加好友 lambda 本身,它有自己的类型,不同于 std::function.

我要做的是将可以访问选定私有部分的内容传递给 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。 class 在我的示例中是私有的,但您可以将其设置为 public,但这是可选的。

你可以像这样实现待办事项功能:

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.
};