我正在编写包含来自其他库的许多功能和方法的库。为了避免复制返回值,我正在应用std::forward
,如下所示:
template<class T>
T&& wrapper(T&& t) {
f(t); // t passed as lvalue
return std::forward<T>(t);
}
f
返回void
并获取T&&
(或价值超载)。包装器总是返回包装器的参数,返回值应该保留参数的值。我是否真的需要在std::forward
中使用return
? RVO会让它变得多余吗?它是参考(R或L)的事实是否是多余的?如果return不是最后一个函数语句(在some if中),是否需要它?
如果wrapper()
应该返回void
或T&&
,那么这是值得商榷的,因为调用者可以通过arg(参考,R或L)访问评估值。但在我的情况下,我需要返回值,以便wrapper()
可以在表达式中使用。
这可能与问题无关,但众所周知,f
函数不会从t
窃取,因此在std::forward
中首次使用f(std::forward<T>(t))
是多余的,它被我删除了。
我写过小测试:https://gist.github.com/3910503
测试显示,返回未转发的T
- 确实在gcc48中创建了额外的副本,并且在-O3中创建了clang32(RVO没有启动)。
此外,我无法在UB中获得不良行为:
auto&& tmp = wrapper(42);
它没有证明任何原因,因为它是未定义的行为(如果它是UB)。
答案 0 :(得分:9)
如果您确实知道t
在调用f
后不会处于移动状态,那么您的两个选择是合理的:
返回类型为std::forward<T>(t)
的{{1}},这样可以避免任何构造,但允许写入,例如T&&
,auto&& ref = wrapper(42);
留下了悬空参考
返回类型为ref
的{{1}},当参数为右值时,最好请求移动构造 - 这可以避免prvalues的上述问题,但可能会从xvalues中窃取
在所有情况下,您都需要std::forward<T>(t)
。不考虑复制省略,因为T
始终是参考。
答案 1 :(得分:6)
根据此函数传递的内容,它会导致未定义的行为!更准确地说,如果将非左值(,即 rvalue)传递给此函数,则返回引用引用的值将过时。
同样T&&
不是“通用参考”,但效果有点像通用参考,因为T
可以推断为T&
或T const&
。有问题的情况是它被推导为T
:参数在函数返回之后作为临时传递并且在函数返回之前死亡,但在任何事情之前都可以获得对它的引用。
std::forward<T>(x)
的使用仅限于在调用另一个函数时转发对象:作为临时函数的内容看起来像函数中的左值。使用std::forward<T>(x)
允许x
看起来像是临时的,如果它是一个 - 因此,允许在创建被调用函数的参数时从x
移动。
当您从函数返回一个对象时,您可能需要处理几个场景,但它们都不涉及std::forward()
:
const
或非const
,则您不希望对该对象执行任何操作,只返回引用。return
语句都使用相同的变量或者所有语句都使用临时变量,则可以使用复制/移动省略,并将用于正常的编译器。由于复制/移动省略是一种优化,因此不一定会发生。std::move()
来允许从本地对象移动。在大多数情况下,生成的类型为T
,您应该返回T
而不是T&&
。如果T
是左值类型,则结果可能不是左值类型,并且可能需要从返回类型中删除引用限定。在您明确询问类型T
工作的方案中。
答案 2 :(得分:3)
不,你不需要使用std::forward
,最好不要返回r值参考,因为它可以阻止NRVO优化。您可以在本文中阅读有关移动语义的更多信息:Article