通过Lambda初始化的C样式函数指针的C ++访问私有成员

时间:2019-01-26 17:08:43

标签: c++ visual-c++

这是一个简单的窗口类(为简便起见,省略了成员):

class window {
public:
    window();
    window(const std::string& title, const gt::size2d& size, bool visible = true, bool fullscreen = false);

    NO_COPY(window);

    window(window&& o);
    window& operator=(window&& o);

    using close_callback = std::function<void()>;

    // members omitted ...

private:
    struct impl;
    struct impl_deleter {
        void operator()(impl* impl);
    };
    std::unique_ptr<impl, impl_deleter> m_pimpl;

    close_callback m_close_callback = []() { DD("Close callback"); };

    // ...
};

我的目标是从GLFW窗口系统调用m_close_callback,我可以实现类似这样的东西:

void close_callback_indirection(GLFWwindow* win)
{
    gt::window* winptr = static_cast<gt::window*>(glfwGetWindowUserPointer(win));
    if (winptr != nullptr) {
        winptr->m_close_callback(); // DOES NOT COMPILE
    }
}

gt::window::window(const std::string & title, const gt::size2d & size, bool visible, bool fullscreen)
    : m_pimpl{ nullptr }, m_close_callback{ []() {} }, m_size_callback{ [](const gt::size2d&) { } }
{
    // omitted GLFW and GL initialization here ...
    GLFWwindow* win = glfwCreateWindow(size.x, size.y, title.c_str(), nullptr, nullptr);
    m_pimpl.reset(new gt::window::impl);
    m_pimpl->glfw_win = win;

    glfwSetWindowUserPointer(win, this);

    glfwSetWindowCloseCallback(win, close_callback_indirection);


    // omitted rest ...
}

这与预期的一样,不会与消息“'gt :: window :: m_close_callback'一起编译:无法访问在类'gt :: window'中声明的私有成员。

但是,如果我像这样实现它:

gt::window::window(const std::string & title, const gt::size2d & size, bool visible, bool fullscreen)
    : m_pimpl{ nullptr }, m_close_callback{ []() {} }, m_size_callback{ [](const gt::size2d&) { } }
{
    // omitted GLFW and GL initialization here ...
    GLFWwindow* win = glfwCreateWindow(size.x, size.y, title.c_str(), nullptr, nullptr);
    m_pimpl.reset(new gt::window::impl);
    m_pimpl->glfw_win = win;

    glfwSetWindowUserPointer(win, this);

    // using lambda instead of function pointer
    glfwSetWindowCloseCallback(win, [](GLFWwindow* win) {
        gt::window* winptr = static_cast<gt::window*>(glfwGetWindowUserPointer(win));
        if (winptr != nullptr) {
            // Accessing private member here
            winptr->m_close_callback(); // WHY THIS WORKS?
        }
    });


    // omitted rest ...
}

现在它可以编译并且可以工作了,如果我按下窗口关闭按钮,我可以看到调试消息。

我的理解是没有捕获列表的lambda可以并且在这种情况下将被强制转换为函数指针,因此我猜编译器将在某个位置生成函数代码并将其传递给指针,但是为什么它可以访问的私有成员窗口对象?生成的函数是window(或朋友)的私有成员吗?

我可以依靠这种行为还是被认为是未定义的?

我正在使用MSVC ++编译器

Microsoft (R) C/C++ Optimizing Compiler Version 19.16.27026.1 for x86

1 个答案:

答案 0 :(得分:2)

所有lambda都可以访问其声明处可以访问的任何内容。如果在类的成员函数中创建一个lambda,则该lambda可以访问该成员函数本身可以访问的任何内容。总是。

将不捕获的Lambda转换为函数指针时,该指针引用的函数与Lambda本身相同。包括其可访问性。