【问题标题】:std::forward test in template and non-template function模板和非模板函数中的 std::forward 测试
【发布时间】:2013-07-09 03:13:53
【问题描述】:

我有以下代码直接来自: 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()) get "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用法!!!

【问题讨论】:

    标签: c++ c++11 perfect-forwarding


    【解决方案1】:

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

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

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

    【讨论】:

      猜你喜欢
      • 2013-08-24
      • 2016-08-22
      • 1970-01-01
      • 2022-01-05
      • 2020-09-04
      • 2017-02-23
      • 2016-04-26
      • 2010-12-20
      • 1970-01-01
      相关资源
      最近更新 更多