是否有使用prvalue的std :: forward的用例?

vso*_*tco 25 c++ rvalue-reference move-semantics c++11

最常见的用法std::forward是,完美地转发转发(通用)引用,例如

template<typename T>
void f(T&& param)
{
    g(std::forward<T>(param)); // perfect forward to g
}
Run Code Online (Sandbox Code Playgroud)

param是一个lvalue,并std::forward最终将它转换为右值或左值,具体取决于与它有关的参数.

看一下cppreference.com定义,std::forward我发现还有一个rvalue重载

template< class T >
T&& forward( typename std::remove_reference<T>::type&& t );
Run Code Online (Sandbox Code Playgroud)

任何人都可以给我任何rvalue超载的理由吗?我看不到任何用例.如果你想将一个右值传递给一个函数,你可以按原样传递它,不需要std::forward在它上面应用.

这与std::move我不同,我明白为什么人们也想要一个rvalue重载:你可能会处理通用代码,在这些代码中你不知道你传递了什么,你想要无条件支持移动语义,请参阅例如为什么std ::移动采取普遍参考?.

编辑为了澄清这个问题,我问为什么从这里需要重载(2),以及它的用例.

Gam*_*per 0

这个答案是为了回答@vsoftco的评论

@DarioOO 感谢您的链接。你能写一个简洁的答案吗?从你的例子来看,我仍然不清楚为什么 std::forward 还需要为右值定义

简而言之:

因为如果没有右值专门化,以下代码将无法编译

#include <utility>
#include <vector>
using namespace std;

class Library
{
    vector<int> b;
public:
    // hi! only rvalue here :)
    Library( vector<int>&& a):b(std::move(a)){

    }
};

int main() 
{
    vector<int> v;
    v.push_back(1);
    A a( forward<vector<int>>(v));
    return 0;
}
Run Code Online (Sandbox Code Playgroud)

但是我无法抗拒输入更多内容,因此这也是答案的不简洁版本。

长版:

您需要移动,v因为该类Library没有接受左值的构造函数,而只有右值引用。如果没有完美的转发,我们最终会出现不良行为:

当传递重物时,包装函数会导致高性能损失。

通过移动语义,我们确保在可能的情况下使用移动构造函数。在上面的例子中,如果我们删除std::forward代码将无法编译。

那么实际上在做什么呢forward?未经我们同意就移动元素?没有!

它只是创建向量的副本并移动它。我们如何确定这一点?只需尝试访问该元素即可。

vector<int> v;
v.push_back(1);
A a( forward<vector<int>>(v)); //what happens here? make a copy and move
std::cout<<v[0];     // OK! std::forward just "adapted" our vector
Run Code Online (Sandbox Code Playgroud)

如果你移动该元素

vector<int> v;
v.push_back(1);
A a( std::move(v)); //what happens here? just moved
std::cout<<v[0];  // OUCH! out of bounds exception
Run Code Online (Sandbox Code Playgroud)

因此,需要重载才能实现仍然安全的隐式转换,但如果没有重载则不可能。

事实上,以下代码将无法编译:

vector<int> v;
v.push_back(1);
A a( v); //try to copy, but not find a lvalue constructor
Run Code Online (Sandbox Code Playgroud)

真实用例:

您可能会认为转发参数可能会创建无用的副本,从而隐藏可能的性能影响,是的,这确实是事实,但请考虑实际用例:

template< typename Impl, typename... SmartPointers>
static std::shared_ptr<void> 
    instancesFactoryFunction( priv::Context * ctx){
        return std::static_pointer_cast<void>( std::make_shared<Impl>(

                std::forward< typename SmartPointers::pointerType>( 
            SmartPointers::resolve(ctx))... 
            )           );
}
Run Code Online (Sandbox Code Playgroud)

代码取自我的框架(第 80 行):Infectorpp 2

在这种情况下,参数是从函数调用转发的。SmartPointers::resolve无论构造函数Impl接受右值还是左值,它的返回值都会被正确移动(因此不会出现编译错误,并且无论如何都会被移动)。

基本上你可以std::foward在任何你想让代码更简单、更具可读性的情况下使用,但你必须记住两点

  • 额外的编译时间(实际上并没有那么多)
  • 可能会导致不需要的副本(当您没有明确地将某些内容移动到需要右值的内容时)

如果小心使用,它是一个强大的工具。

  • 谢谢,顺便说一句,你应该有“Library”而不是“A”。会考虑你的例子,特别是关于*向前制作副本然后移动*。AFAIK,“forward”只是使用参考折叠规则进行转换。但无论如何,再次感谢,很快就会让你知道我的想法。 (2认同)
  • 好的,看这里:http://coliru.stacked-crooked.com/a/1bce1b3c7e5d0237。我用名为“my_forward”的实现更改了“forward”,它**仅适用于**左值。您的代码仍然可以编译。但是,如果您查看 [`std::forward` 的声明](http://en.cppreference.com/w/cpp/utility/forward),您会发现有 2 个重载: (1) 和 ( 2)。(2) 取纯右值。我的问题是为什么(2)是必要的以及什么是用例?如您所见,您的代码适用于仅为左值定义的“my_forward”。我问的有意义吗? (2认同)
  • 在您的情况下,“forward&lt;vector&lt;int&gt;&gt;(v)”与“std::move(v)”相同(请参阅声明和引用折叠规则)。不过,“v”本身仍然是一个左值(有一个名称)。 (2认同)