reference_wrapper<T>
conversion from T&&
Section: 22.10.6 [refwrap] Status: C++20 Submitter: Tim Song Opened: 2017-06-28 Last modified: 2021-02-25
Priority: 3
View all other issues in [refwrap].
View all issues with C++20 status.
Discussion:
reference_wrapper<T>
has a deleted constructor taking T&&
in order to
prevent accidentally wrapping an rvalue (which can otherwise happen with the
reference_wrapper(T&)
constructor if T
is a non-volatile
const
-qualified type). Unfortunately, a deleted constructor can still
be used to form implicit conversion sequences, so the deleted T&&
constructor has the (presumably unintended) effect of creating an implicit conversion sequence from
a T
rvalue to a reference_wrapper<T>
, even though such a conversion would be
ill-formed if actually used. This is visible in overload resolution:
void meow(std::reference_wrapper<int>); //#1
void meow(convertible_from_int); //#2
meow(0); // error, ambiguous; would unambiguously call #2 if #1 instead took int&
and in conditional expressions (and hence std::common_type
) after core issue 1895:
std::reference_wrapper<int> purr(); auto x = true? purr() : 0; // error, ambiguous: ICS exists from int prvalue to // reference_wrapper<int> and from reference_wrapper<int> to int using t = std::common_type_t<std::reference_wrapper<int>, int>; // error: no member 'type' because the conditional // expression is ill-formed
The latter in turn interferes with the use of reference_wrapper
as a
proxy reference type with proxy iterators.
T
rvalues to reference_wrapper<T>
, not just that the conversion will be
ill-formed when used. This can be done by using a suitably constrained
constructor template taking a forwarding reference instead of the
current pair of constructors taking T&
and T&&
.
[2017-06-29, Tim adds P/R and comments]
The draft P/R below uses a conditional noexcept
specification to ensure that converting a T&
to
a reference_wrapper<T>
remains noexcept
and make it not usable when the source type is a
reference_wrapper
of the same type so as to avoid affecting is_trivially_constructible
. It adds a deduction
guide as the new constructor template will not support class template argument deduction.
reference_wrapper<T>
convertible from everything
that is convertible to T&
. This implies, for instance, that reference_wrapper<int>
is now
convertible to reference_wrapper<const int>
when it wasn't before (the conversion would have required
two user-defined conversions previously). This more closely emulates the behavior of an actual reference, but does represent
a change to the existing behavior.
If perfectly emulating the existing behavior is desired, a conditionally-explicit constructor that is only implicit if
T
is reference-compatible with remove_reference_t<U>
(see 9.4.4 [dcl.init.ref]) can be used.
[2017-07 Toronto Tuesday PM issue prioritization]
Priority 3; what else in the library does this affect? ref
or cref
?
[2016-07, Toronto Saturday afternoon issues processing]
Status to Ready.
Proposed resolution:
This wording is relative to N4659.
Edit 22.10.6 [refwrap], class template reference_wrapper
synopsis, as indicated:
namespace std { template <class T> class reference_wrapper { […] // construct/copy/destroyreference_wrapper(T&) noexcept; reference_wrapper(T&&) = delete; // do not bind to temporary objectstemplate <class U> reference_wrapper(U&&) noexcept(see below); […] }; template <class T> reference_wrapper(T&) -> reference_wrapper<T>; […] }
Edit 22.10.6.2 [refwrap.const]/1 as indicated:
reference_wrapper(T& t) noexcept;
-1- Effects: Constructs areference_wrapper
object that stores a reference tot
.template<class U> reference_wrapper(U&& u) noexcept(see below);-?- Remarks: Let
FUN
denote the exposition-only functionsThis constructor shall not participate in overload resolution unless the expressionvoid FUN(T&) noexcept; void FUN(T&&) = delete;FUN(declval<U>())
is well-formed andis_same_v<decay_t<U>, reference_wrapper>
isfalse
. The expression insidenoexcept
is equivalent tonoexcept(FUN(declval<U>()))
. -?- Effects: Creates a variabler
as if byT& r = std::forward<U>(u)
, then constructs areference_wrapper
object that stores a reference tor
.