Как вызвать через указатель на функцию-член?

#c #templates #gcc

#c #функции-указатели

Вопрос:

Я пытаюсь провести некоторое тестирование с помощью указателя на функцию-член. Что не так с этим кодом? bigCat.*pcat(); Инструкция не компилируется.

 class cat {
public:
   void walk() {
      printf("cat is walking n");
   }
};

int main(){
   cat bigCat;
   void (cat::*pcat)();
   pcat = amp;cat::walk;
   bigCat.*pcat();
}
  

Ответ №1:

Требуется больше круглых скобок:

 (bigCat.*pcat)();
^            ^
  

Вызов функции ( () ) имеет более высокий приоритет, чем оператор привязки указателя к члену ( .* ). Унарные операторы имеют более высокий приоритет, чем бинарные операторы.

Комментарии:

1. @AdrianCornish: Нет, но pcat не называет член, он называет указатель на член, объявленный как локальная переменная в main .

2. Я думаю, это немного похоже на вызов виртуальной функции — Есть ли хороший вариант использования / проблема, для которой это полезно — изо всех сил пытаюсь придумать, как использовать это для хорошего кода

3. @AdrianCornish: я никогда не использовал эту функцию и не видел, чтобы она использовалась в коде, написанном другими. Наверняка кто-то его использовал… но вариантов использования немного, и они далеко друг от друга.

4. @AdrianCornish: Это редко используемая функция. Я часто использовал его в общем коде, например, у меня был набор for_each алгоритмов, подобных алгоритмам, которые принимали последовательность указателей и вызывали функцию-член для каждого из объектов, на которые указывали. Это избавило меня от необходимости использовать уродливые заклинания для связывания функций. Это было до того, как лямбда-выражения сделали это намного проще… если бы мне пришлось переписать весь этот код, я бы, вероятно, использовал лямбды для большинства применений.

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

Ответ №2:

Сегодня каноническим способом является использование шаблона функции std::invoke, особенно в общем коде. Пожалуйста, обратите внимание, что указатель на функцию-член идет первым:

 import <functional>;

std::invoke(pcat, bigCat);
  

Что вы получаете: унифицированный синтаксис вызова практически для всего, что можно вызвать.

Накладные расходы: отсутствуют.

Комментарии:

1. Предостережение: требуется C 17 (конечно, все меньше проблем)