何时使用 std::forward 转发参数?
C++0x 显示了使用 std::forward
的示例:
template<class T>
void foo(T&& arg)
{
bar(std::forward<T>(arg));
}
什么时候总是使用 std::forward
有利?
另外,它要求在参数声明中使用 &&
,它在所有情况下都有效吗?我认为如果函数是用 &&
声明的,则必须将临时变量传递给函数,那么可以使用任何参数调用 foo 吗?
最后,如果我有一个像这样的函数调用:
template<int val, typename... Params>
void doSomething(Params... args) {
doSomethingElse<val, Params...>(args...);
}
我应该使用这个来代替:
template<int val, typename... Params>
void doSomething(Params&&... args) {
doSomethingElse<val, Params...>(std::forward<Params>(args)...);
}
另外,如果在函数中使用参数两次,即同时转发到两个函数,那么使用 std:: 是否明智?转发? std::forward
是否会将同一事物两次转换为临时变量,从而移动内存并使其在第二次使用时无效?下面的代码可以吗:
template<int val, typename... Params>
void doSomething(Params&&... args) {
doSomethingElse<val, Params...>(std::forward<Params>(args)...);
doSomethingWeird<val, Params...>(std::forward<Params>(args)...);
}
我对 std::forward 有点困惑,我很乐意进行一些清理。
C++0x shows an example of using std::forward
:
template<class T>
void foo(T&& arg)
{
bar(std::forward<T>(arg));
}
When is it advantageous to use std::forward
, always?
Also, it requires to use &&
in the parameters declaration, is it valid in all cases? I thought you had to pass temporaries to a function if the function was declared with &&
in it, so can foo be called with any parameter?
Lastly, if I have a function call such as this:
template<int val, typename... Params>
void doSomething(Params... args) {
doSomethingElse<val, Params...>(args...);
}
Should I use this instead:
template<int val, typename... Params>
void doSomething(Params&&... args) {
doSomethingElse<val, Params...>(std::forward<Params>(args)...);
}
Also, if use the parameters twice in the function, i.e. forwarding to two functions at the same time, is it wise to use std::forward
? Won't std::forward
convert the same thing to a temporary twice, moving the memory and make it invalid for a second use? Would the following code be ok:
template<int val, typename... Params>
void doSomething(Params&&... args) {
doSomethingElse<val, Params...>(std::forward<Params>(args)...);
doSomethingWeird<val, Params...>(std::forward<Params>(args)...);
}
I'm a bit confused by std::forward
, and I'd gladly use some clearing up.
如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。
绑定邮箱获取回复消息
由于您还没有绑定你的真实邮箱,如果其他用户或者作者回复了您的评论,将不能在第一时间通知您!
发布评论
评论(3)
像第一个示例一样使用它:
这是因为 引用折叠规则:如果
T = U&
,则T&& = U&
,但如果T = U&&
,则T&& = U&&
,因此您始终会在函数体内得到正确的类型。最后,您需要forward
将左值转换的x
(因为它现在有了名称!)返回到右值引用(如果它最初是右值引用)。但是,您不应多次转发某些内容,因为这通常没有意义:转发意味着您可能将参数一直移动到最终调用者,一旦移动,它就消失了,因此您不能再次使用它(以您可能想要的方式)。
Use it like your first example:
That's because of the reference collapsing rules: If
T = U&
, thenT&& = U&
, but ifT = U&&
, thenT&& = U&&
, so you always end up with the correct type inside the function body. Finally, you needforward
to turn the lvalue-turnedx
(because it has a name now!) back into an rvalue reference if it was one initially.You should not forward something more than once however, because that usually does not make sense: Forwarding means that you're potentially moving the argument all the way through to the final caller, and once it's moved it's gone, so you cannot then use it again (in the way you probably meant to).
Kerrek的回答非常有用,但它并没有完全回答标题中的问题:
为了回答这个问题,我们首先应该引入一个概念 通用参考。斯科特·迈耶斯(Scott Meyers)给出了这个名字,现在它们通常被称为转发参考。基本上,当您看到类似这样的内容时:
请记住,
param
不是右值引用(人们可能会想得出这样的结论),而是通用引用*。通用引用的特点是非常受限制的形式(只有T&&
,没有 const 或类似的限定符)和类型推导 - 类型T
将在调用f
时推导。简而言之,通用引用对应于右值引用,如果它们是用右值,以及左值引用(如果它们是用左值初始化的)。
现在回答最初的问题相对容易 - 将
std::forward
应用于:引用 的示例第一种情况:
在上面的代码中,我们不希望
prop
在other.set(..)
完成后有一些未知的值,因此这里不会发生转发。然而,当调用bar
时,我们会在完成后转发prop
,并且bar
可以用它做任何它想做的事情(例如移动它)。第二种情况的示例:
如果它是右值,则此函数模板应将
prop
移动到返回值中,如果它是左值,则应复制它。如果我们在最后省略了 std::forward,我们总是会创建一个副本,当prop
恰好是右值时,它的成本会更高。*准确地说,通用引用是对 cv 不合格模板参数进行右值引用的概念。
Kerrek's answer is very useful, but it doesn't completely answer the question from the title:
In order to answer it, we should first introduce a notion of universal references. Scott Meyers gave this name and nowadays they are often called forwarding references. Basically, when you see something like this:
bear in mind that
param
is not an rvalue reference (as one may be tempted to conclude), but a universal reference*. Universal references are characterized by a very restricted form (justT&&
, without const or similar qualifiers) and by type deduction - the typeT
will be deduced whenf
is invoked. In a nutshell, universal references correspond to rvalue references if they’re initialized withrvalues, and to lvalue references if they’re initialized with lvalues.
Now it's relatively easy to answer the original question - apply
std::forward
to:An example for the first case:
In the code above, we don't want
prop
to have some unknown value afterother.set(..)
has finished, so no forwarding happens here. However, when callingbar
we forwardprop
as we are done with it andbar
can do whatever it wants with it (e.g. move it).An example for the second case:
This function template should move
prop
into the return value if it's an rvalue and copy it if it's an lvalue. In case that we omittedstd::forward
at the end, we would always create a copy, which is more expensive whenprop
happens to be an rvalue.*to be fully precise, a universal reference is a concept of taking an rvalue reference to a cv-unqualified template parameter.
这个例子有帮助吗?我努力寻找 std::forward 的有用的非通用示例,但偶然发现了我们传递的银行帐户的示例
作为参数存入的现金。
因此,如果我们有一个帐户的 const 版本,当我们将其传递给我们的存款模板时,我们应该期望它会发生变化。调用 const 函数;然后抛出一个异常(这个想法是这是一个锁定的帐户!)
如果我们有一个非常量帐户,那么我们应该能够修改该帐户。
构建:
预期输出:
Does this example help? I struggled to find a useful non generic example of std::forward, but hit upon an example of a bank account that we pass along
the cash to be deposited as an argument.
So if we have a const version of an account we should expect when we pass it to our deposit template<> that the const function is called; and this then throws an exception (the idea being this was a locked account!)
If we have a non const account then we should be able to modify the account.
To build:
Expected output: