Title
reference_wrapper<T> conversion from T&&
Status
c++20
Section
[refwrap]
Submitter
Tim Song

Created on 2017-06-28.00:00:00 last changed 38 months ago

Messages

Date: 2017-07-16.20:43:29

Proposed resolution:

This wording is relative to N4659.

  1. Edit [refwrap], class template reference_wrapper synopsis, as indicated:

    namespace std {
      template <class T> class reference_wrapper {
        […]
        // construct/copy/destroy
        reference_wrapper(T&) noexcept;
        reference_wrapper(T&&) = delete;    // do not bind to temporary objects
        template <class U>
          reference_wrapper(U&&) noexcept(see below);
        […]
      };
      template <class T>
      reference_wrapper(T&) -> reference_wrapper<T>;
      […]
    }
    
  2. Edit [refwrap.const]/1 as indicated:

    reference_wrapper(T& t) noexcept;
    

    -1- Effects: Constructs a reference_wrapper object that stores a reference to t.

    template<class U>
      reference_wrapper(U&& u) noexcept(see below);
    

    -?- Remarks: Let FUN denote the exposition-only functions

    void FUN(T&) noexcept;
    void FUN(T&&) = delete;
    
    This constructor shall not participate in overload resolution unless the expression FUN(declval<U>()) is well-formed and is_same_v<decay_t<U>, reference_wrapper> is false. The expression inside noexcept is equivalent to noexcept(FUN(declval<U>())).

    -?- Effects: Creates a variable r as if by T& r = std::forward<U>(u), then constructs a reference_wrapper object that stores a reference to r.

Date: 2017-07-16.20:43:29

[ 2016-07, Toronto Saturday afternoon issues processing ]

Status to Ready.

Date: 2017-07-12.01:58:24

[ 2017-07 Toronto Tuesday PM issue prioritization ]

Priority 3; what else in the library does this affect? ref or cref?

Date: 2017-06-15.00:00:00

[ 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.

The constructor template has the additional effect of making 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 [dcl.init.ref]) can be used.

Date: 2017-06-28.19:32:15

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.

We should ensure that there is no implicit conversion sequence from 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&&.

History
Date User Action Args
2021-02-25 10:48:01adminsetstatus: wp -> c++20
2017-11-13 19:01:36adminsetstatus: voting -> wp
2017-10-17 18:34:55adminsetstatus: ready -> voting
2017-07-16 20:43:29adminsetmessages: + msg9427
2017-07-16 20:43:29adminsetstatus: new -> ready
2017-07-12 01:58:24adminsetmessages: + msg9356
2017-06-28 22:37:15adminsetmessages: + msg9313
2017-06-28 19:32:15adminsetmessages: + msg9312
2017-06-28 00:00:00admincreate