forward
brokenSection: 22.2.4 [forward] Status: Resolved Submitter: Howard Hinnant Opened: 2009-03-13 Last modified: 2016-01-28
Priority: Not Prioritized
View all other issues in [forward].
View all issues with Resolved status.
Discussion:
This is a placeholder issue to track the fact that we (well I) put the standard into an inconsistent state by requesting that we accept N2844 except for the proposed changes to [forward].
There will exist in the post meeting mailing N2835 which in its current state reflects the state of affairs prior to the Summit meeting. I hope to update it in time for the post Summit mailing, but as I write this issue I have not done so yet.
[ Batavia (2009-05): ]
Move to Open, awaiting the promised paper.
[ 2009-08-02 Howard adds: ]
My current preferred solution is:
template <class T> struct __base_type { typedef typename remove_cv<typename remove_reference<T>::type>::type type; }; template <class T, class U, class = typename enable_if< !is_lvalue_reference<T>::value || is_lvalue_reference<T>::value && is_lvalue_reference<U>::value>::type, class = typename enable_if< is_same<typename __base_type<T>::type, typename __base_type<U>::type>::value>::type> inline T&& forward(U&& t) { return static_cast<T&&>(t); }This has been tested by Bill, Jason and myself.
It allows the following lvalue/rvalue casts:
- Cast an lvalue
t
to an lvalueT
(identity).- Cast an lvalue
t
to an rvalueT
.- Cast an rvalue
t
to an rvalueT
(identity).It disallows:
- Cast an rvalue
t
to an lvalueT
.- Cast one type
t
to another typeT
(such asint
todouble
)."a." is disallowed as it can easily lead to dangling references. "b." is disallowed as this function is meant to only change the lvalue/rvalue characteristic of an expression.
Jason has expressed concern that "b." is not dangerous and is useful in contexts where you want to "forward" a derived type as a base type. I find this use case neither dangerous, nor compelling. I.e. I could live with or without the "b." constraint. Without it, forward would look like:
template <class T, class U, class = typename enable_if< !is_lvalue_reference<T>::value || is_lvalue_reference<T>::value && is_lvalue_reference<U>::value>::type> inline T&& forward(U&& t) { return static_cast<T&&>(t); }Or possibly:
template <class T, class U, class = typename enable_if< !is_lvalue_reference<T>::value || is_lvalue_reference<T>::value && is_lvalue_reference<U>::value>::type, class = typename enable_if< is_base_of<typename __base_type<U>::type, typename __base_type<T>::type>::value>::type> inline T&& forward(U&& t) { return static_cast<T&&>(t); }The "promised paper" is not in the post-Frankfurt mailing only because I'm waiting for the non-concepts draft. But I'm hoping that by adding this information here I can keep people up to date.
[ 2009-08-02 David adds: ]
forward
was originally designed to do one thing: perfect forwarding. That is, inside a function template whose actual argument can be a const or non-const lvalue or rvalue, restore the original "rvalue-ness" of the actual argument:template <class T> void f(T&& x) { // x is an lvalue here. If the actual argument to f was an // rvalue, pass static_cast<T&&>(x) to g; otherwise, pass x. g( forward<T>(x) ); }Attempting to engineer
forward
to accomodate uses other than perfect forwarding dilutes its idiomatic meaning. The solution proposed here declares thatforward<T>(x)
means nothing more thanstatic_cast<T&&>(x)
, with a patchwork of restrictions on whatT
andx
can be that can't be expressed in simple English.I would be happy with either of two approaches, whose code I hope (but can't guarantee) I got right.
Use a simple definition of
forward
that accomplishes its original purpose without complications to accomodate other uses:template <class T, class U> T&& forward(U& x) { return static_cast<T&&>(x); }Use a definition of
forward
that protects the user from as many potential mistakes as possible, by actively preventing all other uses:template <class T, class U> boost::enable_if_c< // in forward<T>(x), x is a parameter of the caller, thus an lvalue is_lvalue_reference<U>::value // in caller's deduced T&& argument, T can only be non-ref or lvalue ref && !is_rvalue_reference<T>::value // Must not cast cv-qualifications or do any type conversions && is_same<T&,U&>::value , T&&>::type forward(U&& a) { return static_cast<T&&>(a); }
[ 2009-09-27 Howard adds: ]
A paper, N2951, is available which compares several implementations (including David's) with respect to several use cases (including Jason's) and provides wording for one implementation.
[ 2009-10 Santa Cruz: ]
NAD EditorialResolved. Solved by N2951.
Proposed resolution: