1

我有以下代码直接来自: http ://www.justsoftwaresolutions.co.uk/cplusplus/rvalue_references_and_perfect_forwarding.html

在 g++ 4.8.1 编译为: g++ -std=c++11 testforward.cpp -o testforward.exe

#include <cstdlib>
#include <vector> 
#include <string> 
#include <iostream>   
#include <algorithm> 

class X
{
    std::vector<double> data;
public:
    X():
        data(100000) // lots of data
    {}
    X(X const& other): // copy constructor
        data(other.data)   // duplicate all that data
    {}
    X(X&& other):  // move constructor
        data(std::move(other.data)) // move the data: no copies
    {}

    X& operator=(X const& other) // copy-assignment
    {
        data=other.data; // copy all the data
        return *this;
    }

    X& operator=(X && other) // move-assignment
    {
        data=std::move(other.data); // move the data: no copies
        return *this;
    }
};



void g(X&& t)
{
    std::cout << "t in g is rvalue" << std::endl ;
}
void g(X& t)
{
    std::cout << "t in g is lvalue" << std::endl ;
}

template<typename T>
void f(T&&t)
{
    g(std::forward<T>(t)) ;
}

void h(X &&t)
{
    g(t) ;
}


int main()
{
    X x;
    f(x);   // 1
    f(X()); // 2
    //h(x);  //compile error 
    h(X()); // 3
}

据作者描述如下:

当你将右值引用与函数模板结合起来时,你会得到一个有趣的交互:如果函数参数的类型是对模板类型参数的右值引用,那么如果传递了左值,则类型参数被推断为左值引用,而普通的否则键入...

该测试的结果输出是:

t in g is lvalue
t in g is rvalue
t in g is lvalue

f(x) get "t in g is lvalue" 和预期的一样!!

f(X()) 得到 "t in g is rvalue" ,是的,这就是 std::forward 的用途

h(X()) get "t in g is lvalue",这是我的问题,你可以看到函数 h 不是模板函数,正如作者所描述的“当你将右值引用与函数模板结合起来时,你会得到一个有趣的交互”不是这种情况,这个函数仍然输出“t in g is lvalue”,这意味着这个有趣的交互不仅发生在模板函数中,也发生在正常函数中!!

如果我将代码更改为:

void h(X &&t)
{
    g(std::forward<X>(t)) ;
}

我会得到“t in g is rvalue”!!!

根据测试,我可以说作者描述“当你将右值引用与函数模板结合起来时,你会得到一个有趣的交互”实际上不仅适用于模板函数,它也适用于普通函数,或者我的英语不好,所以我可以没听懂这个描述吧?!

编辑 :

void h(X &&t)
{
    g(t) ;
}

void h(X &t)
{
    g(t) ;
}

h(x);      //get "t in g is lvalue"
h(X());    //get "t in g is lvalue"

=====================================================

void h(X &&t)
{
    g(std::forward<X>(t)) ;
}

void h(X &t)
{
    g(std::forward<X>(t)) ;
}

h(x);      //get "t in g is rvalue"
h(X());    //get "t in g is rvalue"

看起来只有在模板函数中,我会得到 std::forward 的 cprrect 用法!!!

4

1 回答 1

4

h(X &&)中, 的类型t是对 的右值引用X,但命名变量始终被视为左值。因此,即使tX &&,t也不能直接绑定到X &&参数,而只能绑定到X &参数。这是为了安全起见,因为命名变量可以(并且经常)被反复使用。您不希望第一次使用变量来窃取它,即使它最初确实绑定到 r 值。后续使用会看到被窃取的值,这很容易导致代码中的逻辑被破坏。

如果你知道一个变量是一个右值(或者更重要的是,如果你知道你已经完成了它,无论是左值还是右值),将它作为 r- 传递的方式价值是通过使用move(). 的目的forward<T>()是用于通用代码,当您不知道是否应该窃取原始值时。如果您要move()在模板中使用,您可能会不小心盗取左值。所以你改用forward<T>()它,它将解析为无害的传递 ifT是左值类型,并且本质上将等同于move()ifT是非引用或右值引用。

请注意,在您的edit中,您的第二次重载h(即h(X &t))使用forward<>不正确。t在那种情况下的类型是X &,所以你应该使用forward<X&>(t). 如果你这样做了,你会发现它t作为左值传递。但是,在您的两个重载中h,您可以看到第一个有一个右值引用,而第二个有一个左值引用。(不涉及模板推导,因此您知道类型。)因此,您最好move()在第一个重载中直接使用,而在第二个重载中不使用任何内容。的目的forward<T>()是从模板推导中获取信息,以确定它是绑定(并推导出为)左值还是右值。

于 2013-07-09T05:50:10.777 回答