C++ 转发返回值。是否需要转发?
我正在编写一个库,它封装了其他库中的许多函数和方法。为了避免返回值的重叠,我正在应用C++ 转发返回值。是否需要转发?,c++,c++11,return,rvalue-reference,perfect-forwarding,C++,C++11,Return,Rvalue Reference,Perfect Forwarding,我正在编写一个库,它封装了其他库中的许多函数和方法。为了避免返回值的重叠,我正在应用std::forward如下: template<class T> T&& wrapper(T&& t) { f(t); // t passed as lvalue return std::forward<T>(t); } 它不能证明任何原因,因为它是未定义的行为(如果是UB) 不,您不需要使用std::forward最好不要返回r值引
std::forward
如下:
template<class T>
T&& wrapper(T&& t) {
f(t); // t passed as lvalue
return std::forward<T>(t);
}
它不能证明任何原因,因为它是未定义的行为(如果是UB) 不,您不需要使用
std::forward
最好不要返回r值引用,因为它可以防止NRVO优化。您可以在本文中阅读有关移动语义的更多信息:根据此函数传递的内容,它会导致未定义的行为!更准确地说,如果将非左值(即右值)传递给此函数,则返回的引用引用的值将过时
此外,T&&
也不是一个“通用参考”,尽管其效果有点像通用参考,因为T
可以推断为T&
或T常量&
。有问题的情况是当它被推断为T
:参数作为临时传入,并在函数返回后但在任何东西都无法获得对它的引用之前消失
std::forward(x)
的使用仅限于在调用另一个函数时转发对象:作为临时值输入的内容看起来像函数中的左值。使用std::forward(x)
可以让x
看起来像一个临时变量,如果它是作为一个变量出现的,那么就可以在创建被调用函数的参数时从x
移动
从函数返回对象时,可能需要注意一些场景,但没有一个场景涉及std::forward()
:
- 如果类型实际上是一个引用,无论是
还是非const
,您都不想对对象执行任何操作,只需返回引用即可const
- 如果所有
语句都使用相同的变量,或者所有语句都使用临时变量,则可以使用copy/move省略,并且将在适当的编译器上使用。由于复制/移动省略是一种优化,因此不一定会发生return
- 如果始终返回相同的局部变量或临时变量,则可以从中移动它(如果存在移动构造函数),否则将复制对象
- 当返回不同的变量或返回涉及表达式时,仍然可以返回引用,但复制/移动省略将不起作用,如果不是临时的,也不可能直接从结果中移动。在这些情况下,需要使用
来允许从本地对象移动std::move()
在大多数情况下,生成的类型是
T
,您应该返回T
,而不是T&
。如果T
是左值类型,则结果可能不是左值类型,并且可能需要从返回类型中删除引用限定。在本场景中,您特别询问了类型T
是否有效。如果您确实知道T
在调用f
后不会处于“从”状态,则您有两个合理的选择:
- 返回
,类型为std::forward(t)
,避免任何构造,但允许写入,例如t&
,这会留下一个悬空引用auto&&ref=wrapper(42)
- 返回
,类型为std::forward(t)
,在最坏的情况下,当参数为右值时请求移动构造——这避免了上述prvalues问题,但可能会从xvalues中窃取t
在所有情况下,您都需要
std::forward
。不考虑复制省略,因为t
始终是一个引用。Prvalues可能有问题,并且确实会导致过时的引用,但是xvalues不太可能以UB结尾——我并不认为返回moved from值特别有用。如果将右值传递给此函数,则返回的引用引用的值将过时“.我可能很笨,但为什么呢?如果调用方将右值传递给临时值,那么该临时值是否至少在调用方的完整表达式结束之前存在,因此足够长,可以被该函数的返回值引用?因此类似于my\u Foo\u vector.emplace\u back(包装器(Foo())
。我看到返回T
也会允许这样做,我只是看不到返回T&
时会产生一个过时的引用。从概念上讲,您创建的临时对象不是函数看到的对象!从概念上讲,函数看到的是这个对象的副本。参数的生命周期比wh中的完整表达式早结束ich temporary被创建。特别是,5.2.2[expr.call],第4段包含以下句子:“…参数的生命周期在定义它的函数返回时结束…”在实践中,你会看到这个对象的复制/移动被省略。即使我把这个逻辑搞错了,这个对象在概念上是过时的,因为它在被传递的时候被移动了。事实上,我想我已经把自己弄糊涂了:右值引用仍然是一个引用,也就是说,不应该有任何临时对象的副本,它仍然存在ng足以返回对它的引用。尽管如此,仍然存在一个问题,但问题比我想象的还要严重:在调用f(std::forward(t))
中,函数f()
获得从t
移动对象的权限,并在wrapper()中引用t
)
需要假设t
已从中移动。如果f()
未从传递的对象移动,则调用f()
时无需使用std::forward()
。好的,慢慢来:对f()
的调用不应std::forward(t)
如果t
的内容不能被窃取,则发送到被调用函数:转发的目的是使内容能够在
auto&& tmp = wrapper(42);