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