转发返回值.需要std :: forward吗?

Leo*_*sky 31 c++ return rvalue-reference perfect-forwarding c++11

我正在编写从其他库中包含很多功能和方法的库.为了避免复制返回值我正在这样应用std::forward:

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

f返回void并获取T&&(或超载值).包装器总是返回包装器的参数,返回值应该保留参数的值.我是不是真的需要使用std::forwardreturn?RVO会让它变得多余吗?它是参考(R或L)的事实是否是多余的?如果return不是最后一个函数语句(在some if中),是否需要它?

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

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

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

测试显示,返回未转发T- 确实在gcc48和clang32中创建了额外的副本与-O3(RVO没有启动).

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

auto&& tmp = wrapper(42); 
Run Code Online (Sandbox Code Playgroud)

它没有证明任何原因,因为它是未定义的行为(如果它是UB).

Luc*_*ton 9

如果你确实知道t在通话后不会处于移动状态f,你的两个有些明智的选择是:

  • 返回std::forward<T>(t)类型T&&,这避免了任何结构,但允许写,例如auto&& ref = wrapper(42);,留下ref悬挂参考

  • 返回std::forward<T>(t)类型T,最好在参数为右值时请求移动构造 - 这避免了prvalues的上述问题,但可能从xvalues窃取

在你需要的所有情况下std::forward.不考虑复制省略因为t始终是参考.


Die*_*ühl 6

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

T&&并不是一个"通用参考"虽然效果有点像在通用参考T可以推断为T&T const&.有问题的情况是当它被推断为T:参数在函数返回之后作为临时传递而死,但在任何东西都可以获得对它的引用之前.

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

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

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

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

  • "如果将rvalue传递给此函数,则返回的引用引用的值将过时".我可能很密集,但那为什么呢?如果调用者将一个右值传递给一个临时值,那么至少在调用者的完整表达式结束之前是否存在临时值,因此该函数的返回值是否足够长?因此类似于`my_Foo_vector.emplace_back(wrapper(Foo());`.我看到返回`T`也会允许,我只是看不到返回`T &&`产生陈旧的引用. (3认同)