我是C ++新手,我想了解完美转发如何与std::move
一起使用。
我使用模板函数std::vector<QueueData> queue()
定义要填充的fillWithData
。由于我花了一些时间研究完美的转发,我首先要检查我是否理解正确,其次要弄清楚这个背景下的move
行为是什么。
fillWithData
是一个可变参数模板函数,由于forward
,它可以通过折叠规则将参数视为左值或右值。 ( Q1 - 这是正确的吗?)
template< class Container, typename ... Args >
static void fillWithData(Container & oDataContainer,
Args&& ... args) // universal reference
{
typedef typename Container::value_type::element_type QueueDataPtr;
oDataContainer.emplace_back(std::forward<Args>(args)...);
}
现在,假设有一个对象User usr
,可以将参数传递给fillWithData
:
如果我拨打fillWithData( queue, usr.getName(), usr.getEmail(), usr.getAddr() )
,emplace
会拨打以下电话吗?
如果我想进行优化,我可以利用move
将左值作为右值( Q2 - usr.getName()
左值?):
fillWithData( queue,
std::move(usr.getName()),
std::move(usr.getEmail()),
std::move(usr.getAddr()) )
这样,( Q3 )是直接转发给User
构造函数的值,是在向量中直接构造的新对象吗?
此外( Q4 ),例如,如果我在调用usr.getName()
之后对fillWithData
进行了新的调用,数据是否仍然可用? (我想我会遇到运行时错误。)
最后( Q5 ),没有std::move
,实现完美转发是否有意义?
感谢。
答案 0 :(得分:3)
std::move
无法执行完美转发。它将l值引用转换为r值引用。由于它已经执行了强制转换,因此输入与输出不同 - 因此并不完美。
std::forward<X>
执行完美转发,但它要求推导出X的类型(即它是当前函数的模板参数,并且它是一个通用引用)。
有人可能会问为什么必须在看似r值的引用变量名称上调用std::move
。
这是,因为任何带有名称的变量实际上都是l值,无论它是用一个&
还是一个int&& x = y();
声明的。
所以...
int& x = y();
是一个l值,它只能绑定到r值(y必须返回r值引用或临时值)。
std::move(x)
也是一个l值,但它将绑定到l值或r值(y可以返回int&amp;或int&amp;&amp;)。
在上述情况下,std::forward<>
返回r值引用。在第一种情况下,它(似乎)是完美的,在第二种情况下它会被投射 - 因此不能完美地转发。
std::move
检测到差异,而std::forward<T>(x)
则没有。
总之,在推导的上下文中,std::move
将返回与函数中传递的x完全相同的类别(l-valuenes或r-valueness)。参数,而template<class T> void bar(T x); // pass by value
将始终返回r值引用,即使传递了l值引用。
困惑?这是一个简单的经验法则:
假设:
template<class Deduced> void foo(Deduced&& x)
{
// x is a universal reference in deduced context,
// so we probably want to forward it.
bar(std::forward<Deduced>(x));
}
void foo(Known&& x)
{
// x is definitely an r-value reference. No point forwarding something
// we already know the category of - move it.
bar(std::move(x));
}
void foo(Known x)
{
// x is definitely an l-value. No point forwarding an l-value
// reference, as this will cause an un-necessary copy.
// So we must cast x.
bar(std::move(x));
}
void foo(Known const& x)
{
// x is definitely a const l-value reference. No point forwarding
// a const reference, and no point moving it, since a
// (Known const &&) is not useful.
// Whatever we do, there's going to be a copy.
bar(x);
}
然后:
main.py