Perfecting forwarding is an important C++0x technique built atop rvalue references. It allows move semantics to be automatically applied, even when the source and the destination of a move are separated by intervening function calls. Common examples include constructors and setter functions that forward arguments they receive to the data members of the class they are initializing or setting, as well as standard library functions like
make_shared, which “perfect-forwards” its arguments to the class constructor of whatever object the to-be-created
shared_ptr is to point to. At last year’s C&B, I discussed perfect forwarding as the final part of my talk on move semantics and rvalue references.
The thing about perfect forwarding is that there’s both less and more to it than the name suggests. For one thing, perfect forwarding isn’t perfect: there are types that cannot be perfect-forwarded. Examples include
NULL as null pointer constants, as well as braced initializer lists. Such imperfections have implications for both those who specify interfaces as well as those who use them, i.e., for everybody. It’s important to be familiar with the trade-offs of the various design alternatives.
Perfect forwarding is implemented via function templates with parameters declared to be of type
T&&. Such parameters are treated specially during type deduction, which is fine, unless you want to specialize the templates. Purists will rightly point out that you can’t specialize function templates, you can only overload them, but the thing about perfect-forwarding templates is that the only parameter type that gets the special type deduction treatment
T&&, so you can’t overload on, say, something like
T*&& in an attempt to “partially specialize” for pointer types. So what do you do if you’re dealing with a perfect-forwarding template and you want to get the behavior you’d normally get via template overloading?
Even something as simple as writing a perfect-forwarding constructor or setter gets tricky if you want to combine it with something as equally simple as use of the pImpl idiom, because the template instantiation mechanism typically wants all the template code in the header file, yet the use of pImpl is motivated by the desire to avoid having to do that. How do you resolve that problem, especially when what’s supposed to work runs headlong into, um, compiler implementations that are less than what they might be?
In this session, I’ll give a brief review of perfect forwarding (i.e., I won’t assume that everybody saw (and remembers…) my talk at last year’s C&B), then I’ll launch into a discussion of the practical issues such as those above that arise when you try to put perfect forwarding into day-to-day use.
If there are issues related to this topic you’d like to see me address, please let me know via comment on this blog post or via email.