Login | Register   
Twitter
RSS Feed
Download our iPhone app
TODAY'S HEADLINES  |   ARTICLE ARCHIVE  |   FORUMS  |   TIP BANK
Browse DevX
Sign up for e-mail newsletters from DevX


advertisement
 

Solve the Forwarding Problem and Simulate Containers of References-2 : Page 2


advertisement
Demonstrating the Problem
C++98 argument deduction rules don't always let you pass references to functions and algorithms, even when the arguments used are references. This problem is quite common in algorithms that forward their arguments to other callable entities or when constructors forward their arguments to base class constructors. The following code listing demonstrates this problem:

void func(int & ri) { ++ri; } template<class F, class T> void g(F f, T t) { f(t); } int main() { int n = 0; int& rn=n; g(func, rn); cout << rn << endl; // 0 }

You'd expect that after calling g(), the value of n would change to 1. However, func() actually gets a copy of n, not a reference to n. Changing the signature of g() to:

template<class F, class T> void g(F f, T& t)

causes other problems (for instance, when calling g() with rvalue arguments) so this option is ruled out.



This phenomenon is known as the argument forwarding problem. It can lead to bugs that are very hard to detect, especially when working with high-level libraries of algorithms.

Containers manifest a similar problem. Containers of references can be useful in simulating heterogeneous containers—a topic that I've discussed here more than once. However, it's impossible to create such containers in C++98. What may look like a container of references is actually an ordinary container of value objects. This happens because the references are implicitly replaced with value copies of the original argument:

int n=0; int& rn=n; vector <int> vi; vi.push_back(rn); ++vi[0]; // modifies vi's copy of n, not n itself cout <<"original argument: " << rn << endl; // 0 cout <<"vector's copy "<< vi[0] <<endl; // 1

While this behavior is desirable in many cases, there are times when you really need to store references in containers. Changing the template argument to int& certainly won't help because the generation of the vector <int&> specialization produces illegal code constructs such as int& & and int&*:

vector<int&> vi; //compilation error

Enter reference_wrapper
std::tr1::reference_wrapper<T> is a CopyConstructible and Assignable wrapper around a reference to an object of type T. It provides a conversion operator to T&, thereby enabling a function template to work on references unmodified. reference_wrapper can be used in places where argument deduction would not deduce a reference. For example, you can rewrite the previous vector example like this:

#include <utility> using std::tr1::reference_wrapper; int n=0; //simulate a vector of int& vector <reference_wrapper<int> > vi; vi.push_back( reference_wrapper<int>(n)); ++vi[0]; cout <<"original argument: " << n <<endl; // 1 cout <<"vector's reference: "<< vi[0] <<endl; // 1



Comment and Contribute

 

 

 

 

 


(Maximum characters: 1200). You have 1200 characters left.

 

 

Sitemap