首页 > 软件开发 > C++ > 正文
2.3 forward和完美转发
2015-07-07 15:27:19     我来说两句      
收藏    我要投稿

在2.2节中介绍的右值引用类型是独立于值的,一个右值引用参数作为函数的形参,在函数内部再转发该参数的时候它已经变成一个左值了,并不是它原来的类型了。比如:

template <typename T>
void forwardValue(T& val)
{
   processValue(val);   // 右值参数会变成左值
}
template <typename T>
void forwardValue(const T& val)
{
   processValue(val);   // 参数都变成常量左值引用了
}

都不能按照参数的本来的类型进行转发。

因此,我们需要一种方法能按照参数原来的类型转发到另一个函数,这种转发被称为完美转发。所谓完美转发(Perfect Forwarding),是指在函数模板中,完全依照模板的参数的类型(即保持参数的左值、右值特征),将参数传递给函数模板中调用的另外一个函数。C++11中提供了这样的一个函数std::forward,它是为转发而生的,不管参数是T&&这种未定的引用还是明确的左值引用或者右值引用,它会按照参数本来的类型转发。看看这个例子:

代码清单2-4 参数转发的示例

template<typename T>
void PrintT(T& t)
{
   cout <<"lvaue"<< endl;
}

template<typename T>
void PrintT(T && t)
{
   cout <<"rvalue"<< endl;
}

template<typename T>
void TestForward(T && v)
{
   PrintT(v);
   PrintT(std::forward<T>(v));
   PrintT(std::move(v));
}

Test()
{
   TestForward(1);
   int x = 1;
   TestForward(x);
   TestForward(std::forward<int>(x));
}


 

我们来分析一下测试结果。

TestForward(1):由于1是右值,所以未定的引用类型T && v被一个右值初始化后变成了一个右值引用,但是在TestForward函数体内部,调用PrintT(v)时,v又变成了一个左值(因为在这里它已经变成了一个具名的变量,所以它是一个左值),因此,第一个PrintT被调用,打印出“lvaue”。调用PrintT(std::forward<T>(v))时,由于std::forward会按参数原来的类型转发,因此,它还是一个右值(这里已经发生了类型推导,所以这里的T&&不是一个未定的引用类型(关于这点可以参考2.1节),会调用void PrintT(T &&t)函数。调用PrintT(std::move(v))是将v变成一个右值(v本身也是右值),因此,它将输出rvalue。
TestForward(x)未定的引用类型T && v被一个左值初始化后变成了一个左值引用,因此,在调用PrintT(std::forward<T>(v))时它会被转发到void PrintT(T& t)。

右值引用、完美转发再结合可变模板参数,我们可以写一个万能的函数包装器(可变模板参数将在3.2节中介绍,读者不妨读完下一节再回头来看这个函数),带返回值的、不带返回值的、带参数的和不带参数的函数都可以委托这个万能的函数包装器执行。下面看看这个万能的函数包装器。

template<class Function, class... Args>
inline auto FuncWrapper(Function && f, Args && ... args) -> decltype(f(std::for
    ward<Args>(args)...))
{
         return f(std::forward<Args>(args)...);
}
测试代码如下:
void test0()
{
         cout <<"void"<< endl;
}

int test1()
{
         return 1;
}

int test2(int x)
{
         return x;
}

string test3(string s1, string s2)
{
         return s1 + s2;
}

test()
{
   FuncWrapper(test0);   // 没有返回值,打印1
   FuncWrapper(test1);   // 返回1
   FuncWrapper(test2, 1);   // 返回1
   FuncWrapper(test3, "aa", "bb"); // 返回"aabb"
}

 

点击复制链接 与好友分享!回本站首页
您对本文章有什么意见或着疑问吗?请到论坛讨论您的关注和建议是我们前行的参考和动力  
上一篇:2.2 move语义
下一篇:2.4 emplace_back减少内存拷贝和移动
相关文章
图文推荐
排行
热门
文章
下载
读书

关于我们 | 联系我们 | 广告服务 | 投资合作 | 版权申明 | 在线帮助 | 网站地图 | 作品发布 | Vip技术培训 | 举报中心

版权所有: 红黑联盟--致力于做实用的IT技术学习网站