On Wed, Dec 20, 2023 at 11:24 AM Bo Persson via Std-Proposals <std-proposals@lists.isocpp.org> wrote:
On 2023-12-20 at 16:15, Alexander Christensen via Std-Proposals wrote:
> I have been wondering if there could be some benefits in introducing a
> 'readonly' specifier for class member fields. The basic idea is [...]
>
> class FloatRange {
[...]
> private:
>      readonly float _min;
>      readonly float _max;
> };
>
> Normally I'd use 'const' specifier for the two private fields, since I
> would like _some guarantee_ they cannot be modified after creation.
> However, 'const' is a problem for the assignment operator.

Yes, because that would modify the fields.  :-)
Why is assignment allowed to modify, but nothing else?

And even more to the point, these data members are already private. If you want to control what's "allowed," well, you've already done all you can do: what's allowed is literally what the member functions and friends of `FloatRange` permit, and nothing else. You don't have to worry about making them const; they're unmodifiable from outside by virtue of being inaccessible from outside.

However, I note that this is roughly similar to an idea recently floated by Mark Gibbs on the cpplang Slack. There's no good syntax for this, so I'll just make up my own unrealistic fake syntax:
  struct S {
    (public ? const int : int) x_;
    (public ? const int : int) y_;
    static int mf(S);
  };
  int S::mf(S s) {
    static_assert(std::is_same_v<decltype((s.x_)), int&>);
  }
  int nonmf(S s) {
    static_assert(std::is_same_v<decltype((s.x_)), const int&>);
  }

The idea is that inside members and friends of `S`, `s.x_` is `int`; but to the public, it's `const int`.  The defaulted assignment operator (which is a member function of `S`) can still write to `x_`, so `s = {1,2}` will work fine; but `s.x_ = 1` will fail.
The stated benefit of this idea is that because `S`'s members are all publicly accessible, it would still be a structural type, and an aggregate, and standard-layout, and so on. These are all benefits that you'd lose if you simply made `x_` a private member with a public getter method.

Now, I personally don't think that idea's benefit is worth its cost: I like C++'s relatively clean separation between the access control system and the type system, and don't want to see a feature that so blatantly destroys that separation. But it's still closer to being a good idea than OP's `readonly` idea, because it's more general, and easier to form a mental model of. (E.g. we can see at a glance why `operator=` works, and puzzle out why `swap` works, and so on.)

Also, is this a use case/problem big enough to spend the work needed to
change the language? Does it solve a lot of problems, or just some
inconvenience?

Hear, hear.

–Arthur