Почему указатель на нестатический метод класса нельзя вызвать как обычный указатель на функцию?
Указатель на нестатический метод содержит не только адрес вызываемого кода, но и требует отдельного объекта, для которого будет доступен неявный параметр this. Поэтому его нельзя вызвать как обычную свободную функцию: при вызове нужно явно указать объект или указатель на объект.
Указатель на статический метод, напротив, не связан с объектом и по смыслу совместим с обычным указателем на функцию подходящего типа.
Классы C++ объединяют данные объекта и функции, работающие с этими данными. Нестатический метод концептуально получает скрытый параметр this, поэтому вызов метода должен определить, над состоянием какого объекта выполняется операция.
Отдельный тип указателя на нестатический метод позволяет языку сохранить эту связь на уровне типов. Это предотвращает случайную передачу метода туда, где ожидается функция, не имеющая объекта-владельца.
Обычный указатель на функцию описывает вызываемый код без привязки к конкретному экземпляру класса. Нестатический метод такой привязки не имеет заранее: один и тот же метод можно вызвать для разных объектов.
Если смешать эти типы, неясно, откуда брать this. Попытка вызвать нестатический метод без объекта приводит к некорректной форме вызова, а преобразование указателя на метод в обычный указатель на функцию не допускается.
Тип указателя на нестатический метод записывается как указатель на метод конкретного класса и включает его сигнатуру. Само значение такого указателя не является обычным адресом функции; его внутреннее представление также не обязано совпадать с одним машинным адресом.
Вызов выполняется через оператор .*, если есть объект, или ->*, если есть указатель на объект:
Скобки важны: выражения с .* и ->* имеют приоритеты, из-за которых без них вызов может быть разобран не так, как ожидается. Указатель на метод не содержит конкретный объект, поэтому один method можно применить к нескольким объектам типа Worker.
Указатель на метод базового класса может иметь дополнительные ограничения при работе с производными классами. Вызов допустим, если объект совместим с классом, которому принадлежит метод, но правила преобразований указателей на методы сложнее, чем для обычных указателей.
Для передачи метода в универсальный callback часто используют лямбду, std::function или адаптер. Это удобнее, если callback должен хранить конкретный объект вместе с операцией; непосредственный указатель на метод подходит, когда объект передаётся отдельно.
Компонент планировщика принимает callback, который должен запускать метод конкретного обработчика. Рассматривались два варианта: передавать указатель на метод и отдельно указатель на объект либо создать лямбду, захватывающую объект.
Первый вариант не требует дополнительного состояния в callback, но требует от планировщика хранить и корректно сопоставлять две сущности. Лямбда скрывает механизм вызова и удобнее для интерфейса, однако при захвате объекта нужно явно контролировать время его жизни и возможную копию состояния.
Если планировщик не владеет обработчиком и объект гарантированно живёт дольше задания, выбран указатель на метод вместе с указателем на объект. Если же callback должен быть самодостаточным или объект может быть полиморфным, предпочтительнее лямбда с явной политикой владения.
Чем указатель на статический метод отличается от указателя на нестатический метод?
Статический метод не получает this и не обращается к состоянию конкретного объекта через неявный параметр. Поэтому указатель на него можно использовать как обычный указатель на функцию с совместимой сигнатурой. Для нестатического метода требуется объект, и его указатель имеет специальный тип.
Можно ли вызвать указатель на метод для объекта производного класса?
Да, если производный объект доступен как объект совместимого базового класса и соблюдены правила доступа. Указатель указывает на метод базового класса, а объект предоставляет подходящий экземпляр, для которого этот метод может работать. Это не то же самое, что виртуальный вызов: если метод виртуальный, при вызове через указатель на метод действуют правила виртуальной диспетчеризации.
Почему указатель на метод нельзя надёжно хранить в void* или заменять указателем на функцию?
Это разные типы с разными правилами вызова и потенциально разным внутренним представлением. void* предназначен для указателей на объекты и не является универсальным контейнером для указателей на функции или методы. Безопасный вариант — хранить точный тип указателя на метод либо использовать типизированную обёртку, например std::function.