ISO/IEC JTC1 SC22 WG21 N3255 = 11-0025 - 2011-02-27
Lawrence Crowl, crowl@google.com, Lawrence@Crowl.org
Daniel Krügler, daniel.kruegler@googlemail.com
Introduction
Adding to the Standard
20.3 Utility components [utility]
20.3 forward/move helpers [forward]
30.3.1.2 thread constructors [thread.thread.constr]
30.4.4.2 Function call_once [thread.once.callonce]
Fully Postulating
30.4.4.2 Function call_once [thread.once.callonce]
Pablo Halpern noted a problem in meeting his intent when using rvalue references.
I was doing some experimentation and I wrote the following simple-looking function:
template <typename T> void g(T&& v) { std::pair<T,int> x(std::forward<T>(v), 99); f(x); }
I tried calling the above this way:
double d = 5.6; const double cd = 3.4; g(d); g(cd); g(1.2);
and I (correctly) got 3 different types for the pair:
pair<double&, int> pair<const double&, int> pair<double, int>
The problem is that in most cases, what I really want is the last one. I could fix this two ways:
eliminate the perfect forwarding:
template <typename T> void g(const T& v) { std::pair<T,int> x(v, 99); f(x); }
or
use the following gawd-awful construction:
template <typename T> void h(T&& v) { std::pair< typename std::remove_cv< typename std::remove_reference<T>::type >::type , int > x(v, 99); f(x); }
The disadvantage of both should be obvious. This leads me to three standard-related questions:
Are there any recommended best methods for solving this cleanly?
Should any of these best methods result in new components to the standard (perhaps in the next round)? E.g., should there be a remove_reference_and_cv metafunction?
Should we add convenience-wrappers for things like traits classes to simplify this for users, e.g.
template <class Iterator> struct iterator_traits<const Iterator>; template <class Iterator> struct iterator_traits<volatile Iterator>; template <class Iterator> struct iterator_traits<const volatile Iterator>; template <class Iterator> struct iterator_traits<Iterator&>;
Note that
tuple_size
andtuple_element
already have specializations for cv-qualified tuple types, but for reference-to-tuple types. Is there some reason that doing the same for references would be a bad idea?
And the solution was to use the abstract function decay_copy
.
Clark Nelson pointed out
that the standard postulates decay_copy
twice,
but uses it three times.
Given the demonstrated need for the decay_copy
helper function,
we should add it to the standard.
We decided not to add a noexcept
specification
because we have no compile-time trait for it now.
What we would need is std::is_nothrow_convertible
.
The proper declaration would then be:
template <class T> typename decay<Tgt;::type decay_copy(T&& v) noexcept(is_nothrow_convertible<T>::value);
Edit the synopsis as follows.
- Header
<utility>
synopsis.... // 20.3.3, forward/move: template <class T> T&& forward(typename remove_reference<T>::type& t) noexcept; template <class T> T&& forward(typename remove_reference<T>::type&& t) noexcept; template <class T> typename remove_reference<T>::type&& move(T&&) noexcept; template <class T> typename conditional< !is_nothrow_move_constructible<T>::value && is_copy_constructible<T>::value, const T&, T&&>::type move_if_noexcept(T& x) noexcept; template <class T> typename decay<T>::type decay_copy(T&& v); ....
Add a new function as follows.
template <class T> typename decay<T>::type decay_copy(T&& v);
Add a new paragraph as follows.
Effects: equivalent to return
std::forward<T>(v)
.
Add a new paragraph as follows.
Remarks: This function shall not participate in overload resolution unless
T
is implicitly convertible todecay<T>::type
.
Add a new paragraph as follows.
[[Note: See 30.3.1.2 [thread.thread.constr], 30.4.4.2 [thread.once.callonce], and 30.6.9 [futures.async] for examples of use. —end note]
Edit the following thread
constructor as follows.
template <class F, class ...Args> explicit thread(F&& f, Args&&... args);
Given a function as follows:
template <class T> typename decay<T>::type decay_copy(T&& v) { return std::forward<T>(v); }
Edit function call_once
as follows.
template<class Callable, class ...Args>
void call_once(once_flag& flag, Callable&& func, Args&&... args);
Given a function as follows:
template <class T> typename decay<T>::type decay_copy(T&& v) { return std::forward<T>(v); }
If the above solution proves unacceptable, at the very least, we should postulate it for all uses.
Edit function async
as follows.
template <class F, class... Args>
future<typename result_of<F(Args...)>::type>
async(F&& f, Args&&... args);
template <class F, class... Args>
future<typename result_of<F(Args...)>::type>
async(launch policy, F&& f, Args&&... args);Given a function as follows:
template <class T> typename decay<T>::type decay_copy(T&& v) { return std::forward<T>(v); }