c++ - 一个类是否可以包含一个捕获其所在类的 lambda 对象?

标签 c++ constexpr

假设我有一个 Window 类,并且该 Window 类具有 std::function 形式的事件处理程序:

struct Event {};
struct Window
{
    std::function<void(Event)> handler;
};

Window wnd;
wnd.handler = [] (Event) { /*some fancy handler*/ };

从处理程序内部,我无法访问窗口本身,因此我可以将窗口指针传递给处理程序,也可以通过捕获窗口在处理程序函数内部隐式访问该窗口。然而,在捕获案例中,我认为我遇到了这个问题:

struct Event {};
struct Window
{
    std::function<void(Event)> handler;
    int member;
};

int main()
{
    Window wnd1;
    
// Capture the window so I can access it inside the handler function
    Window wnd2{ [&wnd2](Event) { wnd2.member = 3; } };
    
    wnd1 = std::move(wnd2);
    // wnd1 contains a lambda object pointing to wnd2, that's invalid.
    
    Window wnd3 { [&wnd3](int) { wnd3.member = 7; } };
    std::vector<Window> wnd_vector{ wnd3 }; // Now when vector reallocates the lambda // object is invalid because of the 'this' pointer, right?

那么我猜的规则是:如果 lambda 对象已捕获外部类(如果外部类是可移动的),则不能拥有包含 lambda 对象的类。我认为这个问题可以扩展到更一般的情况:

struct MoveableClass
{
    struct Foo { MoveableClass* outer_class_object; }; 
    Foo fooObj{this};
}

我可以使用自定义移动函数来移动此类。但在 std::function 的情况下或者如果它包含 lambda,那么我无法移动它,对吗?

最佳答案

我向您建议这样的解决方案:

#include <iostream>
#include <vector>
#include <functional>

struct Event {};
struct Window
{
public:
    Window() = default;
    Window(std::function<void(Window &, Event)> handler)
      : handler(handler)
    {}
    Window(std::function<void(Window &, Event)> handler,
           int member)
      : handler(handler),
        member(member)
    {}

    void setHandler(std::function<void(Window &, Event)> newHandler)
    {
        handler = newHandler;
    }
    bool callHandler(Event event)
    {
        if (handler)
        {
            handler(*this, event);
            return true;
        }
        return false;
    }
    
    void setMember(int newMember)
    {
        member = newMember;
    }
    int getMember() const
    {
        return member;
    }
private:
    std::function<void(Window &, Event)> handler; // if it is not needed to change class members than you can use "const Window &"
    int member = 0;
};

void print(const Window & obj)
{
    std::cout << "Object: " << static_cast<const void *>(&obj) << ", member: " << obj.getMember() << std::endl;
}

int main()
{
    Event event;
    bool callResult = false;
    
    Window wnd1;
    
    Window wnd2{ [](Window & obj, Event) { obj.setMember(3); } };
    callResult = wnd2.callHandler(event);
    std::cout << "Call result: " << std::boolalpha << callResult << std::endl;
    print(wnd2);
    wnd2.setHandler( [](Window & obj, Event) { obj.setMember(1); } );
    
    
    wnd1 = std::move(wnd2);
    callResult = wnd1.callHandler(event);
    std::cout << "Call result: " << std::boolalpha << callResult << std::endl;
    print(wnd1);
    
    Window wnd3 { [](Window & obj, Event) { obj.setMember(7); } };
    std::vector<Window> wnd_vector{ wnd3 };
    callResult = wnd_vector.front().callHandler(event);
    std::cout << "Call result: " << std::boolalpha << callResult << std::endl;
    print(wnd_vector.front());
    print(wnd3);
    return 0;
}

结果:

Call result: true
Object: 0x7ffd57d9ccb0, member: 3
Call result: true
Object: 0x7ffd57d9cc80, member: 1
Call result: true
Object: 0x2263c30, member: 7
Object: 0x7ffd57d9cce0, member: 0

关于c++ - 一个类是否可以包含一个捕获其所在类的 lambda 对象?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/69235611/

相关文章:

c++ - 无法运行程序 “g++” : Launching failed - Eclipse C++

c++ - 使用 InsertMenuItem() 编辑系统菜单无法正常工作

c++ - regex_match 抛出运行时异常

c++ - 隐藏 constexpr 变量

c++ - 使用 clang 和 g++ 编译此 "simple"程序时出现链接错误

c++ - 如何在 C++ 中拆分一行并从中提取值?

c++ - constexpr 与 const 引用交互的奇怪行为

c++ - 为什么 one-definition-rule-use (odr-use) 取决于上下文?

c++ - 使用非 constexpr 函数设置 constexpr 变量(但可以在编译时计算)

C++ Primer(第5版)初学者的困惑