了解完美转发的片段&可变模板

时间:2014-09-22 07:53:54

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

我看到了来自this answer的代码片段,我似乎无法理解模板和类型名称的排列如何创建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);
}

非常感谢任何帮助。

2 个答案:

答案 0 :(得分:7)

这是一个带有模板 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<int, double>


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

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<int, std::vector<int>>make的上述定义将移动通过std::vector<int>(1000)创建的临时向量到第二个函数参数中。但是,它将复制从那里复制到通过TT<A0, A1>(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<int>(1000)创建的临时向量将被移动到return语句中创建的对象中。


现在,我们可以将此函数模板推广到N个参数;模板 template-parameter 也必须进行通用化,以便您可以传递任何带有一些类型参数的类或别名模板。

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

答案 1 :(得分:2)

代码

的逐行细分
template <template <typename...> class TemplateClass, typename... Args>

TemplateClass本身就是一个需要变量模板参数的模板。如果没有可变参数部分,对于单个参数,它可能看起来像template <typename> class TemplateClass,对于两个参数,它可能看起来像template <typename, typename> class TemplateClassArgs是此函数模板make的可变参数模板参数。

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

返回的类型为TemplateClass<Args...>,从原始模板参数到模板函数,并使用原始模板的可变参数Args进行定义。 make采用名为args的可变参数包,通常称为“通用引用”(&&,在这种情况下,这可能是也可能不是rvalue引用)。 / p>

{
    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函数。它们之所以如此命名,是因为它们可以绑定到“任何”引用(&&&),因为C ++ 11中引入了引用折叠规则。