web-dev-qa-db-ja.com

テンプレートパラメーターとしての汎用メンバー関数ポインター

このコードを考えてみましょう:

#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);
}

もちろん、コンパイラーはRCArgsが何であるかを知らないため、16行目ではコンパイルされません。しかし、別の問題があります。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を適切なテンプレート関数にバインドすることはできません。

解決策はありますか?私の最後の手段は、メンバー関数ポインターを引数として渡すことですが、それをテンプレートパラメーターとして渡すことができれば、コードの残りの部分にうまく適合します。

編集:一部の人が指摘したように、proxycallは引数を渡さないため、この例は無意味に思われます。これは私が取り組んでいる実際のコードには当てはまりません。引数はLuaスタックからいくつかのテンプレートトリックでフェッチされます。しかし、コードのその部分は質問とは無関係であり、かなり長いので、ここでは貼り付けません。

27
Lorenzo Pistone

あなたはこのようなことを試すことができます:

_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);
_
36
Kerrek SB