35

我正在编写包含其他库中的许多函数和方法的库。为了避免处理返回值,我这样应用std::forward

template<class T>
T&& wrapper(T&& t) { 
   f(t);  // t passed as lvalue  
   return std::forward<T>(t);
}

f返回void和接受T&&(或重载价值)。包装器始终返回包装器的参数,并且返回值应保留参数的价值。我真的需要使用std::forwardinreturn吗?RVO 是否让它变得多余?它是参考(R 或 L)这一事实是否使它变得多余?如果 return 不是最后一个函数语句(在某些 if 中),是否需要它?

是否wrapper()应该返回voidor是有争议的T&&,因为调用者可以通过 arg 访问评估值(即引用,R 或 L)。但在我的情况下,我需要返回值,以便wrapper()可以在表达式中使用。

这可能与问题无关,但众所周知,函数f不会从 中窃取t,因此第一次使用std::forwardinf(std::forward<T>(t))是多余的,它被我删除了。

我写过小测试:https ://gist.github.com/3910503

测试表明,返回未转发的T- 确实会在 gcc48 和 clang32 中使用 -O3 创建额外的副本(RVO 不会启动)。

此外,我无法从 UB 获得不良行为:

auto&& tmp = wrapper(42); 

它不能证明任何原因,因为它是未定义的行为(如果它是 UB)。

4

3 回答 3

11

如果您确实知道t在调用 to 后不会处于从状态移出f,则您的两个比较明智的选择是:

  • return std::forward<T>(t)with type T&&,这避免了任何构造,但允许编写 eg auto&& ref = wrapper(42);,这会留下ref一个悬空引用

  • 返回std::forward<T>(t)类型T,当参数是右值时,最坏的情况是请求移动构造——这避免了纯右值的上述问题,但可能会从 xvalues 中窃取

在所有情况下,您都需要std::forward. 不考虑复制省略,因为t它始终是参考。

于 2012-10-18T07:19:23.020 回答
7

根据此函数传递的内容,它会导致未定义的行为!更准确地说,如果您将非左值(右值)传递给此函数,则返回的引用所引用的值将是陈旧的。

也不T&&是“通用参考”,尽管效果有点像通用参考,T可以推断为T& or T const&。有问题的情况是当它被推断为T:参数作为临时传入并在函数返回之后但在任何东西都可以获取对它的引用之前死亡。

的使用std::forward<T>(x)仅限于在调用另一个函数时转发对象:作为临时出现的东西看起来像函数中的左值。使用std::forward<T>(x)letx看起来像一个临时的,如果它作为一个出现 - 因此,x在创建被调用函数的参数时允许移动。

当您从函数返回对象时,您可能需要处理一些场景,但它们都不涉及std::forward()

  • 如果该类型实际上是一个引用,无论是const或非const- ,您都不想对对象做任何事情而只是返回引用。
  • 如果所有return语句都使用相同的变量或所有语句都使用临时变量,则可以使用复制/移动省略并将在体面的编译器上使用。由于复制/移动省略是一种优化,因此不一定会发生。
  • 如果总是返回相同的局部变量或临时变量,则如果存在移动构造函数,则可以从中移动,否则将复制对象。
  • 当返回不同的变量或返回涉及表达式时,仍可能返回引用,但复制/移动省略将不起作用,如果不是临时的,也不能直接从结果中移动。在这些情况下,您需要使用std::move()允许从本地对象移动。

在大多数情况下,生成的类型是T并且您应该返回T而不是T&&. 但是,如果T是左值类型,则结果可能不是左值类型,并且可能需要从返回类型中删除引用限定。在您特别询问类型T作品的场景中。

于 2012-10-18T06:34:57.140 回答
3

不,您std::forward根本不需要使用更好的不返回 r 值引用,因为它可以防止 NRVO 优化。您可以在本文中阅读有关移动语义的更多信息:文章

于 2012-10-18T06:34:53.150 回答