Доступ к закрытому члену C ++ из указателя функции стиля C, инициализированного лямбда-выражением - PullRequest
0 голосов
/ 26 января 2019

Вот простой класс окна (члены для краткости опущены):

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"); };

    // ...
};

Моя цель - вызвать m_close_callback из оконной системы GLFW, и я мог бы реализовать что-то вроде этого:

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

Теперь он компилируется и работает, если я нажимаю кнопку закрытия окна, я вижу сообщение отладки.

Насколько я понимаю, лямбда без списка захвата может и в этом случае будет приведена к указателю на функцию, поэтому я предполагаю, что компилятор где-то сгенерирует код функции и передаст указатель на это, но почему он имеет доступ к закрытому члену оконный объект? Является ли созданная функция приватным членом окна (или другом)?

Могу ли я рассчитывать на это поведение или это то, что считается неопределенным?

Я использую компилятор MSVC ++

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

1 Ответ

0 голосов
/ 26 января 2019

Все лямбды имеют доступ ко всему, что доступно в точке их декларации . Если вы создаете лямбду в функции-члене класса, эта лямбда может получить доступ ко всему, к чему будет иметь доступ сама функция-член. Всегда.

Когда лямбда без захвата преобразуется в указатель функции, функция, на которую указывает этот указатель, идентична самой лямбде. Включая его доступность.

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...