Универсальный указатель на функцию-член в качестве параметра шаблона

Рассмотрим этот код:

#include <iostream>
using namespace std;

class hello{
public:
    void f(){
        cout<<"f"<<endl;
    }
    virtual void ff(){
        cout<<"ff"<<endl;
    }
};

#define call_mem_fn(object, ptr)  ((object).*(ptr))

template<R (C::*ptr_to_mem)(Args...)> void proxycall(C& obj){
    cout<<"hello"<<endl;
    call_mem_fn(obj, ptr_to_mem)();
}

int main(){
    hello obj;
    proxycall<&hello::f>(obj);
}

Конечно, это не скомпилируется в строке 16, потому что компилятор не знает, что R, C а также Args, являются. Но есть и другая проблема: если попытаться определить эти параметры шаблона прямо перед ptr_to_memон сталкивается с этой плохой ситуацией:

template<typename R, typename C, typename... Args, R (C::*ptr_to_mem)(Args...)> 
                             //  ^variadic template, but not as last parameter!
void proxycall(C& obj){
    cout<<"hello"<<endl;
    call_mem_fn(obj, ptr_to_mem)();
}

int main(){
    hello obj;
    proxycall<void, hello, &hello::f>(obj);
}

Удивительно, но на g++ не жалуюсь Args не последний параметр в списке шаблонов, но в любом случае он не может связываться proxycall справа от функции шаблона, и просто отмечает, что это возможный кандидат.

Любое решение? Мое последнее средство - передать указатель на функцию-член в качестве аргумента, но если бы я мог передать его в качестве параметра шаблона, он бы лучше подходил к остальной части моего кода.

РЕДАКТИРОВАТЬ: как некоторые отмечали, пример кажется бессмысленным, потому что прокси-вызов не собирается передавать аргументы. Это не так в реальном коде, над которым я работаю: аргументы извлекаются с некоторыми шаблонными приемами из стека Lua. Но эта часть кода не имеет отношения к вопросу и довольно длинна, поэтому я не буду здесь ее вставлять.

2 ответа

Решение

Вы можете попробовать что-то вроде этого:

template <typename T, typename R, typename ...Args>
R proxycall(T & obj, R (T::*mf)(Args...), Args &&... args)
{
    return (obj.*mf)(std::forward<Args>(args)...);
}

Использование: proxycall(obj, &hello::f);

В качестве альтернативы, чтобы превратить PTMF в аргумент шаблона, попробуйте специализацию:

template <typename T, T> struct proxy;

template <typename T, typename R, typename ...Args, R (T::*mf)(Args...)>
struct proxy<R (T::*)(Args...), mf>
{
    static R call(T & obj, Args &&... args)
    {
        return (obj.*mf)(std::forward<Args>(args)...);
    }
};

Использование:

hello obj;

proxy<void(hello::*)(), &hello::f>::call(obj);

// or

typedef proxy<void(hello::*)(), &hello::f> hello_proxy;
hello_proxy::call(obj);

В современном С++ можно использовать template<auto>и общая лямбда-обертка:

      #include <utility>
#include <functional>

template<auto mf, typename T>
auto make_proxy(T && obj)
{
    return [&obj] (auto &&... args) { return (std::forward<T>(obj).*mf)(std::forward<decltype(args)>(args)...); };
}

struct R {};
struct A {};
struct B {};

struct Foo
{
    R f(A &&, const B &) { return {}; }
    //R f(A &&, const B &) const { return {}; }
};

int main()
{
    Foo foo;
    make_proxy<&Foo::f>(foo)(A{}, B{});
    //make_proxy<static_cast<R (Foo::*)(A &&, const B &) const>(&Foo::f)>(std::as_const(foo))(A{}, B{});
    //make_proxy<static_cast<R (Foo::*)(A &&, const B &)>(&Foo::f)>(foo)(A{}, B{});
}

При наличии перегрузок следует явно указать тип функции-члена, как в закомментированном коде.

Другие вопросы по тегам