Besides providing move semantics, the other main application of rvalue references is in solving “the forwarding problem.” In this context, “forwarding” means passing a generic function’s actual argument on to a second function without rejecting any arguments that can be passed to that second function, without losing any information about the argument’s cv-qualification or l/rvalue-ness, and without overloading. In C++03, the best approximations turn all rvalues into lvalues and require two overloads.
Why Solve This Problem?
Consider this example:
template <class F> struct unary_function_wrapper { unary_function_wrapper(F f) : f(f) {} template <class ArgumentType> void operator()( ArgumentType x ) { f(x); } private: F f; };
f
might accept them. If we change the function to
template <class ArgumentType> void operator()( ArgumentType& x ) { f(x); }
template <class ArgumentType> void operator()( ArgumentType& x ) { f(x); } template <class ArgumentType> void operator()( ArgumentType const& x ) { f(x); }
f can take advantage of the move optimizations
discussed in earlier articles. The need to introduce a second
overload poses an additional problem: it doesn’t scale tractably to
more arguments. A binary_function_wrapper would require four
overloads, a ternary_function_wrapper would require eight, and in
general, perfectly forwarding n arguments requires 2ⁿ overloads.
A Solution That Works
With rvalue references, we can take advantage of some specially-designed language rules to solve the problem this way:
template <class ArgumentType> void operator()( ArgumentType && x ) { f( std::forward<ArgumentType>(x) ); }
A rule for collapsing rvalue references. In C++0x, it was long ago decided that if
TisU&, thenT&is alsoU&. That’s lvalue reference collapsing. Here’s how the rules were updated for rvalue references:&+&yields&&+&&yields&&&+&yields&&&+&&yields&&
That is, any “lvalue-ness” at all makes the result into an lvalue.
A rule for deducing “fully-generalized rvalue reference” parameters like
ArgumentTypeabove. The rule says that if the actual argument is an rvalue,ArgumentTypewill be deduced to be a non-reference type, but if the actual argument is an lvalue,ArgumentTypewill be deduced to be an lvalue reference type.
Here’s how these rules play out when the actual argument is
an rvalue of type Y: ArgumentType is deduced to be Y, so
there’s only one reference and no collapsing: the function parameter
type is Y&&.
When the actual argument is an lvalue of type Y, ArgumentType
is deduced to be Y& (or Y const&) and the reference-collapsing
rules kick in, making the instantiated function’s parameter type Y& &&
or simply Y&… which is consistent with binding to an
lvalue.
The final ingredient is the forward function, whose job is to
“reconstitute” the actual argument’s rvalueness when ArgumentType
is a non-reference and thus pass it on to f without interference.
Like std::move, std::forward is a zero-overhead operation.
Although it’s not an exact translation, you can think of
std::forward<ArgumentType>(x) as a descriptive way to say
static_cast<ArgumentType&&>(x): when the actual argument is an
rvalue, we cast x to an unnamed rvalue reference, but when the
actual argument is an lvalue, ArgumentType is an lvalue reference
and reference-collapsing kicks in, so the target of the
static_cast is also an lvalue reference.
What does “forward” Really Mean?
Recently there’s been substantial disagreement about whether forward
definition should be adjusted to accomodate uses other than “perfect
forwarding,” e.g. to help with the move constructors of types such as
std::tuple, which may contain reference members, and to prevent
dangerous scenarios like binding lvalue references to members of an
rvalue. The proposed adjustments are usually described as helping in
cases where you want to “forward an X as a Y.” For details of these
adjustments, see
N2951.
I’ve never been comfortable with going in this direction because, while
“forward a function’s argument, preserving cv-qualification and
‘rvalue-ness’” makes perfect sense to me, “forward an X as a Y” has
no obvious meaning. In other words, there’s no obvious mental model for
how the proposed forward should be used and what it’s for: we don’t
have a programming model for it. I eventually gave up resisting the
changes because I could see that it was useful in practice for solving a
class of related problems that some users will have to deal with, but I
still think we need to figure out what it means, and how to explain that
clearly.

Hi, It was pointed out by a number of people that reference collapsing rules (that are required for perfect forwarding) are very confusing in other situations, where a type that looks like an r-value reference may in fact turn out to be an l-value reference. For example I write two overloads thereby enabling move-construction optimizations:
Both functions do not modify the argument ‘outside’. The second one will intercept temporaries and reuse them. Now, my algorith was fine and I want to generalize it by making it a template:
The first one probably does what I expect, but the second may in fact take arguments by mutable l-value reference (due to perfect forwarding). I am not sure if I am making sense, as I do not fully understand the tricky behavior of r-val refs, and what happens if “enable_if” tricks or concepts come into play, but it may be dangerous.
I thought that this problem would be solved (in some future C++ with concepts) by an introduction of a yet another concept that would bind ints, floats, pointers, arrays, functions, classes, but no references, and would not allow any CV qualifiers. I couldn’t find any such concept in former drafts. If it had some easy name, like “Object” (I know the name is already taken, but just pretend it isn’t), one could start teaching students C++ templates by saying, “you use templates as follows:
“In a couple of years, when you are an expert, you can use “typename” instead of “Object”, to get more cool features like perfect forwarding, but you have to be aware of perils…” The above “Object” solution has two advantages: (1) it protects against reference collapsing (because we disallow references), and (2) it automatically makes all templates constrained (better error messages, etc.).
Regards, &rzej
(Quote)This is making me wonder too. I object to the special handling of rvalue references. The issue at hand is to perfect forward, right? But why use rvalue references to do this? The issue rvalue references solve is to accept rvalues only, and to make overload resolution prefer them over const lvalues refs for binding from rvalues.
Now your example of “T&&” shows something that’s not expected by most users, i think. Here is what i believe most expect
Why should we treat lvalues any special if the argument during deduction is an lvalue? I’m not understanding all the reasoning of rvalues, i think, nor do i have read all the papers out there about it. But i surely know this is counterintuitive.
I think we should make perfect forwarding use a distinct technique, that only changes deduction and that does not treat rvalues any special. I propose the introduction of “ref template type parameters”. And i would like to know why they weren’t chosen for implementing forwarding (i don’t think i’m the first to come up with this idea). They look like the following:
Deduction will deduce “T” to “U&” for lvalues of type “U”, and to “U&&” for rvalues of type “U”. This will allow perfect forwarding too, without the (imho) cursed special handling of rvalue references:
// factory for T template<typename T, typename ...&U> T create(U ...u) { return T(forward<U>(u)...); }The types in the parameter pack “U” are all either lvalue or rvalue references, depending on the lvalueness of the arguments of the call.
Optionally, one may make “typename &T” forbid making “T” denote a non-reference type. But one can also go another way. Just like one can peel off a pointer modifier if we write “T*” from T, we can peel off an rvalue reference modifier:
A call using an lvalue will deduce T to U&, and a call using an rvalue will deduce T to U (the && is part of the parameter type). Conversely, the following:
A call to f using an rvalue will deduce T to “U&&”, while using an lvalue will deduce T to “U”. An rvalue won’t be accepted if it’s nonconst, since reference collapsing will make the parameter have type “U&” (because “U&& &” == “U&”).
Forward can be defined like the following:
template<typename T> T forward(typename identity<T>::type t) { return static_cast<T>(t); }The “move” function will be written to deduce to reference types:
template<typename &T> typename remove_reference<T>::type && move(T t) { return static_cast<T>(t); } // no cast neededThese proposed changes won’t need reference collapsing necessarily anymore to make perfect forwarding work, i think. But i can’t find a good reason to forbid them – they seem to make sense.
Any opinions on this?
(Quote)The move code is a bit off. I meant the following for move:
template<typename &T> typename remove_reference<T>::type && move(T t) { return static_cast<typename remove_reference<T>::type&&>(t); }(Quote)The short answer is, “because we couldn’t think of another way.” And if you want to know why your idea almost certainly wouldn’t be accepted, it’s just because it’s now too late in the standardization cycle. All I can say is, “where have you been for the past 10 years?” I would have welcomed ideas like this one.
(Quote)I don’t think that’s quite right: I (and others) voiced my preference for not trying to fit everything under the rvalue reference type, and instead introduce forwarding-specific notation. E.g.:
template void ff(for T) { … } // T deduced to & or &&
(I also suggested not introducing rvalue reference types at all, and handle rvalue-vs-lvalue overloading with another parameter modifier.)
(Quote)Hi Daveed,
That’s perfectly true; I wrote imprecisely. There were lots of other ideas raised, but you know as well as I do that there’s worlds of difference between “voicing a preference” and presenting a viable proposal. At one point (the previous Santa Cruz meeting), the committee sent us back to the drawing board to work on trying to realize some of the competing ideas. IIRC Jason Merrill was the only one who showed up for the in-depth conversations and his ideas turned out not to work. In fact, history is littered with approaches people thought were “obviously better,” that turned out to be fatally flawed in some way. I guess after seeing a few of those, I probably wasn’t ready to invest time in proposals that weren’t fairly complete and solid.
(Quote)True.
I just wanted to clarify that we (you, I , Howard, etc.) could think (and had thought) of other ways. There were from the start people (e.g., colleagues and I) who thought the merging of “moving” and “forwarding” was a terrible idea, and we sketched out “other ways” (where “from the start” means during informal discussions, before a formal rvalue reference proposal was made).
My bad for not putting in the energy to push those sketches forward as a competing proposal.
(Quote)Was I around for any of these discussions? Just curious, ’cause I don’t remember them.
(Quote)Yes: I distinctly remember you, Howard, John S., Steve A., and myself having that discussion over lunch (but I don’t recall the venue). (I think there were others — not sure.) I believe that’s also the time when the && token was (mostly) settled on. John and I suggested parameter modifiers both for a bind-reference-to-rvalue mechanism and as a “deduct-to-forward” indication (I, informally, repeated that suggestion a couple of times later as the proposal took shape).
I should note that I did not make the alternative suggestion because of specific issues I foresaw: It was just an intuition that introducing a new kind of reference would have difficult-to-tract ramifications and make the feature less accessible to the C++-programming community. (Had I anticipated the issues you and others have discovered since then, maybe I’d been more forceful.
)
(Quote)Yep, this is a variation on the problem described in http://www.open-std.org/jtc1/sc22/WG21/docs/papers/2008/n2812.html. Of course it depends on #1 somehow being taken out-of-contention, but that can happen. You can avoid trouble by writing
to SFINAE out #2 when Type would be deduced as a reference type, but I grant you, that’s ugly.
(Quote)I don’t quite understand what you mean by:
Won’t #2 always be chosen for non-const lvalues? (gcc with the following code prints “move”)
template< typename Type > void fun( Type const& ) // #1 { std::cout << "copy"; } template< typename Type > void fun( Type && ) // #2 { std::cout << "move"; } int main() { int n; fun(n); }(Quote)Without constraints, yes, #2 will always be chosen non-const lvalues if “#1 isn’t taken out of contention”. What Dave means is to use “constrained templates”. My preference would be to constrain #2 to not accept lvalues (if I really wanted to write code this way):
Now fun(n) outputs “copy”.
Alternatively you could put all of the logic under #2 since it will accept everything:
That “run time if” could also be a compile-time-if:
There are lots of options, and the best one depends on what you want to do. In practice I’ve found that I don’t usually want to overload T&& and const T& for a truly generic T. Far more often T is partially specialized. E.g. I usually want to overload on vector<T>&& and const vector<T>&.
(Quote)I guess the point is that the move/copy overloading pattern only applies when you know how to do the move; otherwise you might as well use a single function, with forwarding, to pass the argument on to something that does know how to move it. A generic
Tisn’t usually something you know how to move from.That said, this looks like another place where concepts reveal a flaw in the rvalue reference design. Something like
where it is known how to move from all models of
SomeConcept, would be a problem. In C++ without concepts, you can have the same problem if you replaceSomeConceptwith a SFINAE constraint:Still, as of today, no real use-case has cropped up that would cause the problem.
(Quote)Reference collapsing is not to blame, IMHO. It’s the template argument deduction rule, §14.9.2.1/3 to be specific. This is going to confuse a lot of people. It already has. I’d also prefer a syntax that explicitly enables this deduction rule (or something similar).
Cheers, Sebastian
(Quote)Thanks, Dave for this article !
I’m currently writting an article on perfect forwading too, for a website in my native langage, and it helps a lot. Even so, I think i will avoid trying to explain the rules of collapsing or the inner mechanism of std::forward because I’m barely understand them.
To illustrate the article, and trying to convey a bit of excitement, I search for cool example in the new standard library which take advange of perfect forwarding. So far, I found :
make_shared : could be done in C++98 (boost does it) but way easier to implement in Ox. If I understand correctly, instead of constructing an object on the heap and pass it to the shared_ptr constructor wich will then allocate a reference counter (in a seperate place of memory), make_shared allow to stick the reference counter and the object in the same place, in one allocation.
“emplace” family function : Like make_shared, somehow take advantage of a delay. Instead of eagerly build a temporary and copy it in a container, delay and construct directly inside the container. Can somehow be done in C++98 (with boost.in_place_factory) but painfully.
operator() of std::ref. Allow to pass an heavy function-object with std::ref to STL algorithms because the std::ref::operator() now forward directly to the operator() of the underlying function-object. I’m not sure if it can be done in C++98, but it’s probably really hard, because even boost doesn’t do it.
std::function, std::bind ? I’m not sure if perfect forwading is used here.
Is there some other beautiful applications of perfect forwarding that I missed in the new SL ?
And trying to see further in the future, is there some brand new stuff that cannot be done in C++98 and are now enabled by perfect forwarding ? If so, can you foreseen some cool application of it ?
(Quote)I recently noticed that all this “perfect” forwarding appears to be a big sham.
Suppose we wish to forward to the following function:
Applying the idiom, we write the wrapper as follows:
template <typename Arg> void wrap(Arg && x) { f(forward<Arg>(x)); }Now suppose have the following template:
Then we can call f with g:
However, the wrapper cannot be called with g:
While the reason this fails is perfectly clear and understandable, it seems to me the conclusion is equally clear: this notion that C++1x’s rvalue references and std::forward facilitate “forwarding” (defined in the post as “passing a generic function’s actual argument on to a second function without rejecting any arguments that can be passed to that second function”), is a lie.
(Quote)Hi Eelis,
Thanks for your post.
I see your point about function templates, but to call “perfect forwarding” a “big sham” is perhaps overstating the case a bit. I think:
I also think you overstate your case in labelling “a lie” the claim that these features facilitate forwarding. They certainly do make forwarding possible and relatively easy, even if the result (or the article’s definition of forwarding) is ever so slightly imperfect.
(Quote)Hi Dave,
Thanks for the reply.
You’re right, of course, that my rhetoric was a bit excessive. It’s just that I’m excited about C++1x, and finding out about warts like these makes me sad and a little emotional.
As for your third point; I think it’s not that esoteric at all. After all, one uses exactly this conversion every time one uses “cout << endl” (or other stream manipulators)! Indeed, that is the context in which I originally encountered this problem.
(Quote)More specifically, my use case was that of a simple unary print() function which streams its argument to std::cout. I had hoped that C++1x’s improved forwarding facilities would make it possible to write print() as a single function, but alas, it appears one still needs to write some overloads to make print(std::endl) work.
(Quote)Exactly. That is the only context in which one commonly encounters the use of this (mis-)feature. Anywhere else, a sensible person would have defined
endlas an object instance, with a templatedoperator(). The fact thatendllooks like an ordinary object in common code, and yet is not, is incredibly confusing when it breaks in generic contexts. TryI haven’t bothered to look at the hoops they had to jump through in Boost.Phoenix to get this code to work as expected. And in case you think this is somehow the fault of Boost,
doesn’t compile either, for the same reasons. I maintain that this is an esoteric feature that probably should never have been put in the language to begin with, since we can do the same thing much more uniformly with function objects.
(Quote)Hm, putting the blame on the conversion feature is an interesting perspective that I had not considered. I agree it’s a defensible position. So I’ll agree that C++1x supports perfect forwarding for all “sanitary” cases not involving that conversion, while it’s merely an unfortunate historical accident that the standard library’s streams interface is insanitary.
(Quote)As much as I love perfect forwarding, there is a more common case that fails that I’m sure plenty of people will accidentally bump into:
void foo( int* ); template< typename ParamType > void bar( ParamType&& arg ) { foo( std::forward< ParamType >( arg ) ); } int main() { foo( 0 ); // Fine bar( 0 ); // ***Kaboom*** }Still, almost perfect is perfect enough.
(Quote)Wow, this case is far worse indeed! This is bad..
(Quote)I don’t think it’s as bad as you’re making it out to be. You only have a right to expect these kinds of use cases to work in contexts where you already know the signature of the wrapped function (or its overload set). The normal case (e.g. in algorithms that invoke function objects) is that the caller already has to assume that
(Quote)0will be interpreted as an int, etc. So, e.g.,make_sharedis slightly imperfect, but things like bind and lambda don’t suffer.It seems a lot worse than it really is. I can’t imagine this being a roadblock for anyone, it’s more just an annoyance since you’d have to do an explicit cast (or pass an object that is convertible to int*).
(Quote)Another case is this one:
struct A { static int const value = 1; }; void g(int); template<typename T> void f(T&&t) { g(forward<T>(t)); }Then calling
is fine, but calling
will fail to find a definition of A::value.
(Quote)Care to explain why you think so? This code compiles fine under g++-4.4.1 with
-std=c++0x:We are not immediately reading a value from A::value, so A::value is considered “used” (3.2/2), and you need to define it. Trying to link the program makes g++4.4.2 show this error to me:
“main1.cpp:(.text+0×11): undefined reference to `A::value’”
(Quote)Point taken… but again not particularly serious for reasons already mentioned.
(Quote)From reading the N2951 paper, it seems to me that “forward<X>(y)” means “convert X to type Y without leaving a dangling reference”. The name forward does not connote this at all, but, by design, that is the effect of the function.
(Quote)That’s not quite right, but it’s close. The following wouldn’t leave a dangling reference even if it did compile.
I think it would be simpler to say “without binding an lvalue reference to an rvalue.”
Also, it’s not arbitrary type conversions, e.g.
(Quote)forwardwon’t convert achar const*to astd::string. But I bet if you keep pushing in that direction you can come up with something understandable and concise.OK, how about “convert y to type X without binding an lvalue reference to an rvalue, creating a new object, or casting away const or volatile”.
(Quote)Wouldn’t any explanation also have to note that whether X is an lvalue reference or non-reference makes the expression an lvalue or rvalue respectively?
(Quote)You’re getting warmer, doc! Do we have to explain that
forward<X>(y)returns an rvalue reference, or do you suppose that’s implied by “without…creating a new object?”Also, note that while we may in fact now have an accurate description of the language mechanics, it only points to the primary use-case through many layers of indirection. I’d rather call such a thing something
(Quote)_cast.Is it really needed to explicitly write the template parameter? Can’t the template parameter for std::forward be deduced by the argument?
(Quote)Assuming you mean “deduced from the argument to
forward,” not “deduced from the actual argument being forwarded,” then: nope. As noted in an earlier article, a named rvalue reference is an lvalue.* In fact, anything with a name is an lvalue. So you need some additional information to re-construct “rvalue-ness” of the actual argument being forwarded, and that information is captured in the deduced typeArgumentType(in the examples from this article).* In light of my response to Rodrigo, I should probably have written more precisely: “any expression that consists of a name is an lvalue expression”
(Quote)Hi Dave!
In my opinion, forward feels like a hack. I understand the “named rvalue” rule and I agree with it but… its not natural. I think its the only place in C++ where a type refuses to act like itself. When you teach rvalues you need to show the rationale of the rule too.
I agree with your opinion about the role of forward. A better name for a function that changes the type is actually forward_as, quoting you in “forward an X as a Y”. Maybe we should have both functions? I don’t know if its worthy.
(Quote)I think your sense of discomfort probably comes from a misunderstanding about rvalues, and possibly from the name “rvalue reference.” I’m guessing you don’t know that r/lvalue-ness” is not a property of types, but of expressions, and that using the name “rvalue reference” for a type only reinforces your misperception. Am I correct, and does that help a bit?
Naturally. I don’t think that’s problematic, do you?
Then I guess you don’t really agree with my opinion
. I don’t care what role
(Quote)forwardhas as long as it’s sensible and explainable, and it can be used to do what I understand to be forwarding. I don’t want to standardize anything we can’t explain, and since I’ve said nobody has explained to me what “forward X as a Y” means, I’d be especially unhappy with aforward_asfunction at this time. I would actually prefer to hide that additional functionality—if we must accept it without a clear explanation—under the nameforward, since at least I think I understand what “forward the actual argument” means.I understand the rvalue stuff but what is a property of expressions and not of types made its place as a type signature. Indeed, the term rvalue reference is confusing (I had a hard time learning the real nature of move semantics). However I should say the proposal was beautiful because it could fit in C++ in a non-invasive way.
About forward, I’d prefer to simply hide the extra functionality too. Otherwise it’s more a cast than a forward.
But teaching forwarding_as is easy anyway!
its just a generalization of forward.
(Quote)forward_as, not forwarding_as -_-
(Quote)Not really; rvalue itself never shows up as a “type signature” (if I understand what you mean by that phrase). It’s rvalue reference that appears in types. You just have to remember that “rvalue reference” is just a name for a kind of reference with different binding rules and slightly different meaning in expressions (it is treated as an rvalue unless named).
I’m not sure what you mean by “hide,” but I just meant “consolidate.”
OK, then, teach it to me, because I don’t get it.
A table of rules doesn’t work; that just leaves me with the idea that it’s a cast with some seemingly-arbitrary possibilities ruled out. The nearest I’ve been able to come to understanding it is something like: “this is what you use when you’re writing converting copy constructors and assignments for types with parts of arbitrary type that may turn out to be reference types, e.g.
(Quote)tuple<int&, long&, char const&>.” I don’t find that to be a very satisfying mental model for this operation. It seems like an arcane special case and makes me wonder why we’re expending valuable standard wording on it. I know that it’s not a contrived usage scenario, but it’s rare, and for me the value of standardizing it is strongly counterbalanced by its complexity.Special cases are the ones that make some things hard to learn and teach (even if not that hard, thats worse than no exceptions to the rules).
In my ideal world, forward would be a language statement – forward(v) instead of forward{type_of_v}(v) – so I would rule out any intention to allow an arbitrary cast there. Even a macro is nicer
@define forward(a) static_cast{decltype(a)}(a)
(hash and brackets changed to avoid wordpress problems) and you need less typing. Not sure if it works always (and macros are evil) but typing the full std::forward form is annoying sometimes.
Just my opinion.
(Quote)I think you’d probably want that to be
(Quote)static_cast<decltype(a)&&>(a), but that’s an interesting take on it.Probably yes, I only tested it quickly in gcc with a small piece of code and seemed to work.
(Quote)