Указатели на функции-члены с одним выражением

#c #c 20

#c #c 20

Вопрос:

Стандартным способом представления указателя на функцию-член для конкретного экземпляра класса является использование указателя на экземпляр и указателя на функцию-член:

 void Execute(Foo* inst, void (Foo::*func)(int), int x) {
  (inst->*func)(x);
}
...
Execute(foo, amp;Foo::Bar, 42);
  

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

Например:

 void Execute(SomeType v, int x) {
  (v.inst->*v.func)(x);
}
...
Execute(foo->Bar, 42);
  

Моя основная проблема заключается в том, что в моем конкретном случае Foo вложен в длинную и нестабильную цепочку пространств имен, поэтому фактический вызов с использованием стандартного синтаксиса выглядит скорее как Execute(foo, amp;hello::darkness::my::old::friend::Foo::Bar, 42) . Однако у меня почти всегда есть локальный foo экземпляр, из которого я могу обратиться к гораздо более простому foo->Bar(42) . Однако мне нужно провести дополнительную бухгалтерию, поэтому мне нужно обернуть вызов чем-то вроде Execute . using к сожалению, директивы и псевдонимы пространства имен не являются опцией.

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

1. Как просто указатель на член, нет. Но вы можете легко написать тип, который связывает функции-члены

2. @Human-Compiler правильно, но тогда я не могу фактически вызвать его в конкретном экземпляре. Также по-прежнему требуется указывать все пространство имен. Или вы говорите, что это можно перевести foo->Bar на amp;hello::darkness...::Foo::Bar ?

3.Читая это снова, я, возможно, неправильно понял вопрос. Если вы хотите избежать ввода полного имени типа при попытке доступа к указателю на член, вам либо понадобится using псевдоним, чтобы попытаться сократить его, либо, в лучшем случае, использовать amp;decltype(foo)::Bar , чтобы избежать полного имени

4. Я не уверен, действительно ли это возможно, но были бы вы довольны чем-то вроде Execute(foo, Bar, 42); ?

5. @Human-Compiler Я подумал, что мог бы использовать некоторую decltype магию, но в конечном итоге это должно выглядеть так amp;std::remove_reference<decltype(*foo)>::type::Bar , что все еще довольно грубо. Есть ли способ скрыть эту проблему в реализации (без использования макросов)?

Ответ №1:

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

ДА. Измените функцию на use std::function или вызываемый параметр шаблона, например:

 #include <functional>

void Execute(std::function<void(int)> v, int x)
{
    v(x);
}
  
 template<typename Callable>
void Execute(Callable v, int x)
{
    v(x);
}
  

И затем вы можете использовать std::bind() или лямбда-выражение для входного выражения, например:

 #include <functional>

using namespace std::placeholders;
using FooType = std::remove_reference<decltype(*foo)>::type;

Execute(std::bind(amp;FooType::Bar, foo, _1), 42);
// or, in C  20 and later:
Execute(std::bind_front(amp;FooType::Bar, foo), 42);
  
 Execute([=](int x){ foo->Bar(x); }, 42);
  

Кроме того, просто чтобы упомянуть, что в C Builder, в частности, есть __closure расширение, которое позволяет вызывать методы-члены без необходимости указывать тип класса вообще, например:

 typedef void (__closure *TExecuteCallable)(int);

void Execute(TExecuteCallable v, int x)
{
    v(x);
}

...

Execute(foo->Bar, 42);
// or
Execute(amp;(foo->Bar), 42);
  

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

1. Небольшое примечание: C 20 имеет std::bind_front , что предпочтительнее для тяжеловесного инструмента, который std::bind : std::bind_front(amp;FooType::Bar, foo)

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

3. @Человек-компилятор действительно? FooType Псевдоним with decltype не решает эту проблему? Или тот факт, что лямбда-выражение вообще устраняет необходимость в этом?

4. Я думаю, что, должно быть, изначально неправильно истолковал ваш ответ; не нужно защищаться. Я не понял, что FooType это псевдоним, и я неправильно истолковал лямбда-выражение как все еще использующее функции-члены. Мой плохой!

5. @Крис добавил, спасибо. Тем не менее, лямбда-выражение обычно предпочтительнее любого использования std::bind...()