C++ Logo

std-proposals

Advanced search

Re: [std-proposals] Disable assignment to an rvalue

From: Arthur O'Dwyer <arthur.j.odwyer_at_[hidden]>
Date: Mon, 17 Oct 2022 13:06:27 -0400
On Mon, Oct 17, 2022 at 12:52 PM Edward Catmur via Std-Proposals <
std-proposals_at_[hidden]> wrote:

> On Mon, 17 Oct 2022 at 17:31, Lénárd Szolnoki via Std-Proposals <
> std-proposals_at_[hidden]> wrote:
>
>> On 17 October 2022 12:38:32 BST, "Peter Sommerlad (C++) via
>> Std-Proposals" <std-proposals_at_[hidden]> wrote:
>> >
>> >struct X{};
>> >auto & dangle = (X{} = X{});
>>
>> The main issue here seems to be to return a reference from the assignment
>> at all. And if `rvalue = something` returns a reference, it should better
>> be an rvalue reference. If the object is expiring, then it is still
>> expiring after you assign to it.
>>
>
> But we write the copy assignment operator as X& X::X(X&), and the move
> assignment operator as [X&] X::X(X&&); the implicit object parameter is not
> ref-qualified, so it can accept any value category, but for historical
> reasons it always returns lvalue. And this is what the
> automatically-generated assignment operators do as well.
>
> Are you proposing to double the number of required/supported assignment
> operator overloads?
>

I think the first-order proposal here is for user-defined types to *stop
returning a reference from operator= at all.* Programmers should just be
able to return `void` from operator= wherever possible. Unfortunately that
can't easily be supported for historical reasons:

    struct X {
        void operator=(const X&) = default; // Proposal: Make this compile
    };

    struct RuleOfZero {};
    RuleOfZero z;
    z = z = z; // Caveat: But this C/C++98 code must continue to compile
successfully

And C++20 Ranges made the situation exponentially worse by *requiring* all
user-defined iterator types to proliferate the "reference-returning
assignment" antipattern. So now such a proposal would also need to change
the library side of things, too:

    struct It {
      using value_type = int;
      using difference_type = int;
      It();
      It(const It&);
      void operator=(const It&); // N.B.: void, not It&; this line breaks
the static_assert below
      int operator*() const;
      It& operator++();
      void operator++(int); // N.B.: this line is actually already OK in
C++20
    };
    static_assert(std::input_iterator<It>); // Proposal: make this
static_assert compile

    template<std::input_iterator It>
    It user_defined_algorithm(It& first) {
      return first = It(); // Proposal: designate this C++20 code as
undesirable, and actively break it
    }

I think these changes would all be great, but I think the relevant ship
sailed in C++98 and then pretty much fell off the edge of the earth in
C++20.

–Arthur

Received on 2022-10-17 17:06:39