了解功能特征模板的工作原理。特别是指向成员函数的指针是怎么回事

Posted

技术标签:

【中文标题】了解功能特征模板的工作原理。特别是指向成员函数的指针是怎么回事【英文标题】:Understanding how the function traits template works. In particular, what is the deal with the pointer to member function 【发布时间】:2015-12-15 08:09:47 【问题描述】:

我正在尝试了解this code,我在这个主题上找到了some more SO content。

紧凑形式:

#include <tuple>
namespace sqlite 
    namespace utility 
        template<typename> struct function_traits;

        template <typename Function>
        struct function_traits : public function_traits<
            decltype(&Function::operator())
        >   ;

        template <
            typename    ClassType,
            typename    ReturnType,
            typename... Arguments
        >
        struct function_traits<
            ReturnType(ClassType::*)(Arguments...) const
        > 
            typedef ReturnType result_type;

            template <std::size_t Index>
            using argument = typename std::tuple_element<
                Index,
                std::tuple<Arguments...>
            >::type;

            static const std::size_t arity = sizeof...(Arguments);

        ;
    

这使我了解了该语言的指向成员函数的特性,并且在这一点上我很清楚如何使用函数特征(这相当简单,我不仅可以提取返回类型,还可以提取参数甚至参数的类型),但我一直在理解为什么这有效,或者实际上为什么它甚至可能有效.. .

【问题讨论】:

具体有什么不明白的? 【参考方案1】:

让我们逐个分解。

template<typename> struct function_traits;

这是主要的模板声明,用一个模板参数声明一个模板类。

template <typename Function>
struct function_traits : public function_traits<
    decltype(&Function::operator())
>   ;

这本质上是一个转发特征,允许函子(具有operator() 的对象)与此特征类一起使用。它是从operator() 的特征继承的主模板的定义。当后面定义的特化不匹配时,会选择这个版本。

template <
    typename    ClassType,
    typename    ReturnType,
    typename... Arguments
>
struct function_traits<
    ReturnType(ClassType::*)(Arguments...) const
>

这开始了 function_traits 的部分特化定义,用于指向成员函数的指针。当您将成员函数指针类型作为模板参数传递给function_traits 时,将选择此特化。返回类型、类类型和参数类型将被推导出为模板参数。

    typedef ReturnType result_type;

这是一个简单的typedef,它将推导的ReturnType模板参数别名为result_type

    template <std::size_t Index>
    using argument = typename std::tuple_element<
        Index,
        std::tuple<Arguments...>
    >::type;

这就是所谓的别名模板。当您提供诸如function_traits&lt;myFunc&gt;::template argument&lt;2&gt; 之类的索引时,它将折叠到typename std::tuple_element&lt;2, std::tuple&lt;Arguments...&gt;&gt;::type。这样做的目的是在索引Index 处提取参数的类型。作者选择使用std::tuple 中的现有代码,而不是手动编写所有模板元编程代码。 std::tuple_element 提取元组的第 n 个元素的类型。

   static const std::size_t arity = sizeof...(Arguments);

sizeof... 用于获取可变参数模板参数包的大小,因此该行将函数的参数数量存储在静态整数成员 arity 中。

【讨论】:

【参考方案2】:

我确实意识到包含ClassType::* 的部分是为了绑定到decltype(&amp;Function::operator())。我正在学习这个构造(指向成员函数的指针),但真的很难理解为什么它的存在很重要。

ReturnType(ClassType::*)(Arguments...) const是一个模板类型名,在这种情况下它是要匹配一个指向成员函数的指针,并且它被巧妙地构造成绑定到operator()成员函数,允许模式匹配为我们提取相关类型,即 args 和返回类型。 ClassType 可能会被识别为与 Function 模板参数相同的东西。

我还对为什么需要一个看起来很有趣的模板化 using 语句来查找参数类型值感到困惑。然后我意识到Arguments... 是一个可变参数模板类型构造,它必须使用编译时特性进行元组化,并且整个业务与“真实”元组无关。当我查看 tuple_element 时,它确实是一种在编译时操作类型的工具。

我不确定这个答案的其余部分

我确实想知道为什么使用指向成员函数的指针。我猜原因可能是常规函数指针或某些此类构造实际上不能用于匹配函子的operator () 成员,因为成员函数需要对象的实例才能变得可调用。 This answer 对这个主题有更多的了解。我想我现在基本明白了。

【讨论】:

以上是关于了解功能特征模板的工作原理。特别是指向成员函数的指针是怎么回事的主要内容,如果未能解决你的问题,请参考以下文章

这个指向成员转换的指​​针有啥问题?

错误:'指向成员的指针对托管类无效'

指向成员的指针对托管类无效

具有指向成员函数的指针的 SFINAE 类型特征失败

指向模板类成员函数的函数指针

将指向外部类模板的成员函数的指针传递给嵌套类