首页 > 解决方案 > 如何为非成员和成员函数指针部分专门化结构模板

问题描述

我正在做一些涉及函数指针的模板元编程。因为非成员函数指针和成员函数指针的函数类型不同,所以我试图在两者之间进行专门研究。此外,我希望将指向函数的指针作为非类型模板参数提供,而不是作为结构中函数的参数。到目前为止,这是我尝试过的:

template <typename T, T>
struct register_method;

template <typename R, typename... Args>
struct register_method<R(Args...), R (*method)(Args...)>
{
    static R invoke(Args&&... params)
    {
        return (*method)(std::forward<Args>(params)...);
    }
};

template <typename T, typename R, typename... Args>
struct register_method<R(Args...), R (T::*method)(Args...)>
{
    static R invoke(T* instance, Args&&... params)
    {
        return (instance->*method)(std::forward<Args>(params)...);
    }
};

但是,这无法编译(这只是第一个错误):

prog.cc:14:48: error: 'Args' does not refer to a value
struct register_method<R(Args...), R (*method)(Args...)>
                                               ^
prog.cc:13:35: note: declared here
template <typename R, typename... Args>
                                  ^

我不确定它想告诉我什么。我的目标是像这样使用这些对象:

void the_func(float val)
{
    std::cout << "the_func called: " << val << "\n";
}

int main()
{
    register_method<decltype(&the_func), &the_func>::invoke(50.f);
}

我怎样才能得到这个工作?如果有机会简化,那也太好了(例如,如果我可以将函数指针传递给模板而不是也做 a decltype,这应该减少样板文件。

编辑:还想补充一点,我需要对非成员函数和成员函数进行单独的专门化的原因不仅仅是函数类型不同。由于我附加到不同功能类型的业务规则,两者之间存在不同的静态状态。我在这里省略了这些细节以保持问题简单。

标签: c++

解决方案


以下是修复代码的方法:

template <typename T, T>
struct register_method;

template <typename R, typename... Args, R (*method)(Args...)>
struct register_method<R (*)(Args...), method>
{
    template <typename ...P>
    static R invoke(P &&... params)
    {
        return (*method)(std::forward<P>(params)...);
    }
};

template <typename T, typename R, typename... Args, R (T::*method)(Args...)>
struct register_method<R (T::*)(Args...), method>
{
    template <typename ...P>
    static R invoke(T *instance, P &&... params)
    {
        return (instance->*method)(std::forward<P>(params)...);
    }
};

请注意,您必须引入一个单独的参数包才能使转发引用起作用,因为它们仅在推导模板参数时才起作用。


这是使用 C++17auto模板参数的替代解决方案:

template <auto method>
struct register_method;

template <typename R, typename... Args, R (*method)(Args...)>
struct register_method<method>
{
    template <typename ...P>
    static R invoke(P &&... params)
    {
        return (*method)(std::forward<P>(params)...);
    }
};

template <typename T, typename R, typename... Args, R (T::*method)(Args...)>
struct register_method<method>
{
    template <typename ...P>
    static R invoke(T *instance, P &&... params)
    {
        return (instance->*method)(std::forward<P>(params)...);
    }
};

推荐阅读