Welcome

首页 / 软件开发 / VC.NET / VC10中的C++0x特性 Part 2 (3):右值引用

VC10中的C++0x特性 Part 2 (3):右值引用2010-05-29 vcblog Stephan T. Lavavej本文为 Part 2 的第三页

转发问题

在程序员不用写高度泛化的代码的时候,C++98/03 的 lvalue,rvalue,引用,还有模板看起来是很完美的。假设你要写一个完全泛化的函数 outer(),这个函数的目的是将任意数目个任意类型的参数传递(也就是“转发”)给函数 inner()。已有很多不错的解决方案,比如 factory 函数 make_shared<T>(args) 是把 args 传给 T 的构造函数,然后返回 shared_ptr<T>。(这样就把 T 对象和用于对它进行引用计数的代码存储到同一块动态内存中,性能上与侵入式引用计数一样好); 而像 function<Ret(args)> 这样的包装类是把参数传给其内部存储的函数对象(functor),等等。在这篇文章里,我们只对 outer() 是如何把参数传递给 inner() 这部分感兴趣。至于 outer() 的返回类型是怎么决定的是另外的问题(有时候很简单,如 make_shared<T>(args) 总是返回 shared_prt<T>,),但要在完全搞定这个问题的一般化情况,你就要用到 C++0x的 decltype 特性了)。

如果不带参数,就不存在这样的问题,那么带一个参数情况呢?让我们尝试写个 outer() :

template <typename T> void outer(T& t) {
inner(t);
}

问 题来了,如果传给它的参数是非常量 rvalue,那我们就无法调用 outer()。如果 inner() 接收 const int& 型的参数,那 inner(5) 是可以通过编译的,但是 outer(5) 就编译不过了。因为 T 会被推导为 int,而 int& 是不能绑定到常量 5 的。

好吧,让我们试试这个:

template <typename T> void outer(const T& t) {
inner(t);
}

如果 inner()接收 int& 型参数,那就会违法 const 正确性,编译都过不了。

现在,你可以重载两个分别带 T& 和 const T& 参数的 outer(),这确实管用。当你调用 outer()时,就像直接调用 inner() 一样。

可惜的是,这中方法在多参数的情况下就麻烦了(译注:要写的重载函数太多了)。你就得为每一个参数像 T1& 和 const T1&, T2& 和 const T2& 等这样进行重载,要重载的函数数目呈指数级增长。(VC9 SP1 的 tr1::bind() 就够让人感到绝望了,它为 5 个参数这么重载出了 63 个函数。如果不这么蛮干的话,没有像这里的长篇累述,我们就很难跟使用者解释为什么不能调用用 1729 这样的 ravlue 做参数的函数。为了产生出这些重载函数使用了令人作呕的预处理机制,恶心到你都不想知道它)。

在 C++98/03 中,转发问题是很严重的,而且本质上无解(必须求助于恶心的预处理机制,这会严重拖慢编译速度,还让代码变得难以阅读)。总算,rvalue 优雅地解决了这个问题。