给出“C ++编程语言第4版”中的以下函数模板:
template <typename TT, typename A>
unique_ptr<TT> make_unique(int i, A && a)
{
return unique_ptr<TT>{new TT{ i, std::forward<A>(a) }};
}
我发现很难理解它实际上是做什么的,
a绝对是右值,因此是make_unique函数 似乎在堆上分配其内容并将该地址保存在unique_ptr中,因此我们不必担心删除它。但是,标准库转发功能有什么作用? (我想它与rvalue有关)我试着阅读C ++文档,但我似乎并不理解。 我很乐意从一位经验丰富的C ++程序员那里得到一个很好的解释。
谢谢!
答案 0 :(得分:2)
嗯......我很确定这不是作为未来std::make_unique
的解决方案实现的,但无论如何,该功能的功能很容易理解,尽管它要求你事先了解新的C ++ 11功能。
template <typename TT, typename A>
unique_ptr<TT> make_unique(int i, A && a)
{
return unique_ptr<TT>{new TT{ i, std::forward<A>(a) }};
}
首先make_unique
是一个功能模板,我真的希望你已经知道,因为以下需要你至少拥有最多关于模板的作用以及模板如何工作的基本知识。
现在到了非平凡的部分。 A && a
有一个函数参数。具体而言,a
是函数参数,其类型为A&&
,即r-value reference。由于其类型是模板类型参数,我们可以从调用者作为a
的参数传递的任何内容推导出它的类型。每当我们有r值引用和参数类型推导,特殊推理规则和参考折叠启动时,我们就会有一个所谓的“通用引用”,这对于完美的转发功能特别有用。
每当我们有一个通用引用(在我们的例子中为a
)时,我们几乎总是希望在我们想要使用它们时保留其原始的“l值”或“r值”。要有这种行为,我们几乎应该总是使用std::forward
(std::forward<A>(a)
)。通过使用std::forward
,最初作为l值传递的变量仍然是l值,而最初作为r值传递的变量仍然是r值。
之后,事情很简单
return unique_ptr<TT>{new TT{ i, std::forward<A>(a) }};
注意大括号的使用。它使用C ++ 11的uniform initialization语法来调用构造函数,而不是使用括号。使用new TT{ i, std::forward<A>(a) }
,您将使用大括号内的给定参数动态分配类型为TT
的对象。使用unique_ptr<TT>{new TT{ i, std::forward<A>(a) }};
,您将创建一个unique_ptr<TT>
,其参数是动态分配返回的参数。然后,现在从函数返回unique_ptr<TT>
对象。
答案 1 :(得分:1)
由于模板参数推导和参考折叠规则,您无法知道a
是左值引用还是左值引用。 std::forward
将参数传递给TT控制器,就像它传递给make_unique一样。 Scott Meyers将A&&
称为universal reference,因为它可以是左值ref或rvalue ref,取决于传递给make_unique的内容。
如果您将右值Foo
传递给make_unique
,std::forward
会传递左值参考。
如果您将左值Foo
传递给make_unique
,std::forward
会传递左值参考。
make_unique(1, Foo()); // make_unique(int, A&&) -> rvalue ref
Foo f;
make_unique(1, f); // make_unique(int, A&&&) -> make_unique(int, A&) -> lvalue ref
make_unique(1, std::move(f)); // make_unique(int, A&&&&) -> make_unique(int, A&&) -> rvalue ref