【问题标题】:how to declare properly the template taking function type as a parameter (like a std::function)如何正确声明以函数类型为参数的模板(如 std::function)
【发布时间】:2016-12-25 08:26:27
【问题描述】:

我发现拥有像这样的简洁语法并非易事:

std::function<int(float, bool)>

如果我将函数声明为:

template <class RetType, class... Args>
class function {};

定义函数的模板类型是一种普通的语法:

function<int,float,bool> f;

但它使用部分模板专业化的奇怪技巧

template <class> class function; // #1

template <class RV, class... Args>
class function<RV(Args...)> {} // #2

这是为什么呢? 为什么我需要给模板一个带有空类型参数(#1)的空通用表单,否则它就不会编译

【问题讨论】:

    标签: c++ templates std-function


    【解决方案1】:

    这正是语言的设计方式。主模板不能像那样对类型进行复杂的分解;您需要使用部分专业化。

    如果我理解正确,您希望只编写第二个版本而无需提供主模板。但是想想模板参数如何映射到模板参数:

    template <class RV, class Arg1, class... Args>
    class function<RV(Arg1, Args...)> {}
    
    function<int(float,bool)>; //1
    function<int, float, bool>; //2
    

    选项1 是您要编写的内容,但请注意,您将单个函数类型传递给模板,其中参数是两个类型参数和一个类型参数包。换句话说,在没有主模板的情况下编写它意味着您的模板参数不一定与模板参数匹配。选项2 匹配模板参数,但不匹配特化。

    如果你有多个专业,这就更没意义了:

    template <class RV, class Arg1, class... Args>
    class function<RV(Arg1, Args...)> {}
    
    template <class T, class RV, class Arg1, class... Args>
    class function<RV (T::*) (Arg1, Args...)> {}
    

    你也许可以想出一些规则来从专业中推断出主要模板,但这对我来说似乎很糟糕。

    【讨论】:

    • 我明白了......所以模板参数只是类型(或类型参数包或其他)的命名占位符。但它应该与实际类型出现的实际用法/专业化相匹配,对吧?
    • @barney 是的,模板参数本质上是占位符。使用的特化需要匹配主模板的模板参数列表。
    【解决方案2】:

    您必须记住int (float, bool) 是一个类型。更准确地说,它是“函数类型,它接受floatbool 类型的两个参数,并返回int。”

    既然是类型,很明显模板在你想使用int (float, bool)语法的地方必须有一个类型参数。

    同时,只有函数类型很笨拙。当然,你可以轻松做到。例如,如果您需要做的只是某种转发器:

    template <class T>
    struct CallForwarder
    {
      std::function<T> forward(std::function<T> f)
      {
        std::cout << "Forwarding!\n";
        return f;
      }
    };
    

    然而,一旦你想访问函数类型的“组件”,你就需要一种方法来为它们引入标识符。最自然的方法是对函数类型进行部分特化,就像您在问题中所做的那样(就像 std::function 所做的那样)。

    【讨论】:

    • 是的,确实有道理。因此,通过在“模板”列表中引入它们,可以对使用标识符命名它们的类型的内部组件进行部分特化,对吗?所以基本上这个列表不是正在使用的实际类型的列表,即如果我声明模板 但在实际函数参数列表中使用“模板 void process(std::function f) ;"模板的类型是 process_manangled_name__function_from_t,而不是 T 本身,对吗?
    • @barney 对于第一个问题,是的。部分特化允许您提供模板的替代定义,在参数符合特定模式(例如我们的例子中的函数类型)时使用。第二个问题我不太明白:-(
    【解决方案3】:

    您实际上可以这样做:

    template <typename T>
    class X { };
    
    X<int(char)> x;
    

    X 的定义中,您可以像创建std::function&lt;int(char)&gt; 一样创建std::function&lt;T&gt;。这里的问题是你不能(至少很容易地)访问返回类型和参数的参数类型(intchar 这里)。

    使用“技巧”,您可以毫无问题地访问这些 - 它“简单地”使您的代码更清晰。

    【讨论】:

      【解决方案4】:

      “为什么我需要给模板一个带有空类型参数的空通用表单”

      因为,您希望在“返回类型”和“参数类型”之间进行显式类型区分,并具有与语法糖相似的清晰性。在 C++ 中,template class 的第一手版本没有这样的语法。您必须对其进行专门化,才能区分它。

      看完你的Q,我看了&lt;functional&gt;头文件。 即使std::function 也会这样做

      // <functional>  (header file taken from g++ Ubuntu)
      
      template<typename _Signature>
      class function;
      
      // ...
      
       /**
         *  @brief Primary class template for std::function.
         *  @ingroup functors
         *
         *  Polymorphic function wrapper.
         */
      template<typename _Res, typename... _ArgTypes>
      class function<_Res(_ArgTypes...)>
      

      正如您在他们的 cmets 中看到的,他们将专用版本视为“主要”类。因此,这个技巧来自标准标头本身!

      【讨论】:

        猜你喜欢
        • 2018-09-02
        • 1970-01-01
        • 2021-12-31
        • 2019-01-30
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        相关资源
        最近更新 更多