接受所有成员函数指针的模板(包括 CV 限定和 ref 限定)

Template accepting all member function pointers (including CV-qualified and ref-qualified)

我想编写一个模板,它接受一个 指向成员函数的指针 (可能是 CV 限定 and/or ref 限定)同时匹配所有相关类型(return 值的类型,class 类型和参数的类型)。

简单版本可能如下所示:

template <typename ReturnValue, typename Class, typename... Arguments>
void foo(ReturnValue(Class::* function_ptr)(Arguments...))
{
    // do something with |function_ptr|
}

这适用于非 CV 限定的非 ref 限定的 成员函数指针 ,但无法与指向 CV 限定的 and/or ref- 的指针匹配合格的成员函数。

有没有一种好的方法可以将它保存在一个模板中并通过以某种方式很好地推导限定符来处理所有情况?


我可以降级为一个接受所有内容的简单模板(可能在 std::is_member_function_pointer 附近有一些 SFINAE)。但我仍然需要在某处提取类型(return、class 和参数),这样实际上不会节省我任何工作。

如何将成员函数指针传递给 foo 并使用辅助特征我们检索 class、return 和参数的类型!

提供如下特征:

template<typename Class> struct class_traits final {};
template<typename ReType, typename Class, typename... Args>
struct class_traits<ReType(Class::*)(Args...)> final
{
    using class_type = Class;
    using ret_type = ReType;
};    

// traits helpers
template<typename MemFunctionPtr> using  class_type = typename class_traits<MemFunctionPtr>::class_type;
template<typename MemFunctionPtr> using  ret_type = typename class_traits<MemFunctionPtr>::ret_type;

现在在foo

#include <functional> // std::invoke

template <typename MemFuncType, typename... Args>
void foo(MemFuncType&& func, Args&&... args) 
{
    // class type can be retrieved!
    class_type<MemFuncType> obj{};

    // types other than void!!
    ret_type<MemFuncType> res = std::invoke(func, obj, std::forward<Args>(args)...); 

    // do something with |function_ptr|

}

现在是故事的主要部分。以上只会解析非const、非volatile、非noexcept等版本的成员函数指针。您问的正是这个问题!

对于其余的,我们需要提供不同的特征。受到 的启发,我们可以通过宏来实现,它将为我们完成特征的样板复制:

template<typename Class> struct class_traits final {};
#define CREATE_CLASS_TRAITS(...) \
template<typename ReType, typename Class, typename... Args> \
struct class_traits<ReType(Class::*)(Args...)__VA_ARGS__> final  \
{ \
    using class_type = Class; \
    using ret_type = ReType; \
}

CREATE_CLASS_TRAITS();
CREATE_CLASS_TRAITS(const);
CREATE_CLASS_TRAITS(volatile);
CREATE_CLASS_TRAITS(const volatile);
CREATE_CLASS_TRAITS(&);
CREATE_CLASS_TRAITS(const&);
CREATE_CLASS_TRAITS(volatile&);
CREATE_CLASS_TRAITS(const volatile&);
CREATE_CLASS_TRAITS(&&);
CREATE_CLASS_TRAITS(const&&);
CREATE_CLASS_TRAITS(volatile&&);
CREATE_CLASS_TRAITS(const volatile&&);
CREATE_CLASS_TRAITS(noexcept);
CREATE_CLASS_TRAITS(const noexcept);
CREATE_CLASS_TRAITS(volatile noexcept);
CREATE_CLASS_TRAITS(const volatile noexcept);
CREATE_CLASS_TRAITS(&noexcept);
CREATE_CLASS_TRAITS(const& noexcept);
CREATE_CLASS_TRAITS(volatile& noexcept);
CREATE_CLASS_TRAITS(const volatile& noexcept);
CREATE_CLASS_TRAITS(&& noexcept);
CREATE_CLASS_TRAITS(const&& noexcept);
CREATE_CLASS_TRAITS(volatile&& noexcept);
CREATE_CLASS_TRAITS(const volatile&& noexcept);
#undef CREATE_CLASS_TRAITS

// traits helpers
template<typename MemFunctionPtr> using  class_type = typename class_traits<MemFunctionPtr>::class_type;
template<typename MemFunctionPtr> using  ret_type = typename class_traits<MemFunctionPtr>::ret_type;

现在我们可以:

template <typename MemFuncType, typename... Args>
void foo(MemFuncType&& func, Args&&... args) 
{
    class_type<MemFuncType> obj{};

    using Type = decltype(std::invoke(func, obj, std::forward<Args>(args)...));
    static_assert(std::is_same_v<ret_type<MemFuncType>, Type>, "are not same");

    std::invoke(func, obj, std::forward<Args>(args)...);

    // do something with |function_ptr|
}

class MyClass
{
public:
    void func() { std::cout << "MyClass::void func()\n"; }
    void func1() const { std::cout << "MyClass::void func1() const\n"; }
    void func2() const noexcept{ std::cout << "MyClass::void func2() const noexcept\n"; }
    int func3() const { std::cout << "MyClass::func3() const\n";  return {}; }
    int func4(int a, double b) const { std::cout << "MyClass::func3() const"<< a << " " << b << "\n";  return {}; }
};

int main()
{
    foo(&MyClass::func);
    foo(&MyClass::func1);
    foo(&MyClass::func2);
    foo(&MyClass::func3);
    foo(&MyClass::func4, 1, 2.);
    return 0;
}

(See Live Demo Online)