【问题标题】:Understanding a snippet of perfect forwarding & variadic template了解完美转发和可变参数模板的片段
【发布时间】:2014-09-22 07:53:54
【问题描述】:

我从this answer 看到了一个sn-p 这段代码,我似乎无法理解模板和类型名的排列如何创建main() 中显示的最终函数调用。

特别是可变参数模板模板的排列和 std::forward 的使用让我感到困惑。

任何人都可以分解这段代码以便我更好地理解它吗?

#include <utility>

template <template <typename...> class TemplateClass, typename... Args>
TemplateClass<Args...> make(Args&&... args)
{
    return TemplateClass<Args...>(std::forward<Args>(args)...);
}

int main() 
{
  make<std::pair>(1, 2);
}

任何帮助将不胜感激。

【问题讨论】:

  • 如果删除所有... 是否更有意义?有时更容易理解这些示例是删除“可变参数”部分,然后将它们添加回来(它们将细节添加到需要扩展的内容和位置)。

标签: c++ templates typedef variadic-templates perfect-forwarding


【解决方案1】:

这是一个带有模板的函数模板template-parameter

template <template <typename, typename> class TT>
TT<int, double>
make(int i, double d)
{
    return TT<int, double>(i, d);
}

您指定一个具有两个类型参数的类或别名模板,此函数将使用此模板的特化,使用intdouble 作为模板参数创建。例如:

make< std::pair >(42, 4.2);

这会返回一个std::pair&lt;int, double&gt;


我们现在可以“模板化”这个函数的(函数)参数:

template <template <typename, typename> class TT, typename Arg0, typename Arg1>
TT<Arg0, Arg1>
make(Arg0 a0, Arg1 a1)
{
    return TT<A0, A1>(a0, a1);
}

模板参数Arg0Arg1 旨在从用于调用函数的(函数)参数的类型推导出来:

int i = 42;
double d = 4.2;
make< std::pair >(i, d); // returns a `std::pair<int, double>`

对于更复杂的数据类型,我们可能希望使用完美转发:

make< std::pair >( 42, std::vector<int>(1000) );

这将返回一个std::pair&lt;int, std::vector&lt;int&gt;&gt;make 的上述定义会将通过std::vector&lt;int&gt;(1000) 创建的临时向量移动 到第二个函数参数中。但是,它会从那里复制到通过TT&lt;A0, A1&gt;(a0, a1) 创建的对象中。

我们可以改变make的定义来实现完美转发:

template <template <typename, typename> class TT, typename Arg0, typename Arg1>
TT<Arg0, Arg1>
make(Arg0&& a0, Arg1&& a1)
{
    return TT<A0, A1>(std::forward<A0>(a0), std::forward<A1>(a1));
}

通过std::vector&lt;int&gt;(1000) 创建的临时向量将被移动到返回语句中创建的对象中。


现在,我们可以将这个函数模板泛化为 N 个参数;模板 template-parameter 也必须被泛化,以便您可以传递任何带有一些类型参数的类或别名模板。

template <template <typename...> class TemplateClass, typename... Args>
TemplateClass<Args...> make(Args&&... args)
{
    return TemplateClass<Args...>(std::forward<Args>(args)...);
}

【讨论】:

    【解决方案2】:

    逐行分解代码

    template <template <typename...> class TemplateClass, typename... Args>
    

    TemplateClass 本身就是一个需要可变模板参数的模板。如果没有可变参数部分,对于单个参数,这可能看起来像 template &lt;typename&gt; class TemplateClass,对于两个参数,可能看起来像 template &lt;typename, typename&gt; class TemplateClassArgs 是这个函数模板make 的可变参数模板参数。

    TemplateClass<Args...> make(Args&&... args)
    

    返回类型为TemplateClass&lt;Args...&gt;,从原始模板参数到模板函数,并使用原始模板的可变参数Args 定义。 make 采用称为 args 的可变参数包,通常被称为“通用引用”(&amp;&amp;,在这种情况下最终可能是也可能不是右值引用)。

    {
        return TemplateClass<Args...>(std::forward<Args>(args)...);
    }
    

    返回类型的对象是使用构造函数创建的,该构造函数接受最初提供给函数的所有参数。

    完美转发用于确保为最初收到的每个参数维护值类别。 ... 扩展了它左侧的内容,因此对于参数包中的每个参数,应用 std::forward(这是必需的,因为 std::forward 只接受一个参数。

    perfect forwarding 是如何工作的?

    简而言之,引用折叠和static_cast 的组合。基本上,如果参数是左值引用,std::forward 的返回类型也是左值引用。如果是右值引用,则返回类型是右值引用(类似于std::move 的结果)。

    "universal references"(或“转发引用”)如何工作?

    它们为std::forward 和类似功能的机制提供动力,例如本例中的make。另一个例子是标准库中的std::make_shared 函数。它们之所以如此命名,是因为它们可以绑定到“任何”引用(&amp;&amp;&amp;),因为 C++11 中引入了引用折叠规则。

    【讨论】:

      猜你喜欢
      • 2011-09-23
      • 1970-01-01
      • 2013-01-06
      • 1970-01-01
      • 1970-01-01
      • 2013-08-25
      • 2016-03-22
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多