Вот простой класс окна (члены для краткости опущены):
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