C++ Logo

std-proposals

Advanced search

Re: [std-proposals] readonly specifier

From: Arthur O'Dwyer <arthur.j.odwyer_at_[hidden]>
Date: Wed, 20 Dec 2023 14:34:15 -0500
On Wed, Dec 20, 2023 at 11:24 AM Bo Persson via Std-Proposals <
std-proposals_at_[hidden]> 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
<https://cpplang.slack.com/archives/C2PQKRWJU/p1702907818935739> 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

Received on 2023-12-20 19:34:29