模板和非模板函数中的std :: forward test

时间:2013-07-09 03:13:54

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

我直接来自以下代码: 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)得到“t in g is lvalue”就像预期的那样!!

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

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

如果我将代码更改为:

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

我会得到“是在r值”!!!

Accorind测试,我可以说作者描述“当你将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用法

1 个答案:

答案 0 :(得分:3)

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

如果你知道一个变量是一个r值(或更多的点,如果你知道你已经完成它,无论它是一个l值还是一个r值),那么传递它的方式是r值是使用move()。当您不知道是否应该窃取原始值时,forward<T>()的目的是用于通用代码。如果您在模板中使用move(),则可能会意外地窃取l值​​。因此,如果forward<T>()是l值类型,则会使用T来解析为无害传递,如果move()T,则基本上等同于h非参考或r值参考。

请注意,在修改中,您h(X &t)的第二次重载(即forward<>)错误地使用了t。在这种情况下,X &的类型为forward<X&>(t),因此您应该使用t。如果你这样做,你会发现h作为l值传递。但是,在move()的两个重载中,您可以看到在第一个中有一个r值引用,在第二个中你有一个l值引用。 (不涉及模板推理,所以你知道类型。)因此,你也可以直接在你的第一次重载中使用forward<T>(),而不是在你的第二次重载中使用任何东西。 {{1}}的目的是从模板推导中获取信息,以确定它是否被绑定(并推断为)l值或r值。