C++ Logo


Advanced search

Re: [wg14/wg21 liaison] Multidimensional subscript operator

From: Jens Maurer <Jens.Maurer_at_[hidden]>
Date: Sun, 25 Apr 2021 22:52:25 +0200
On 25/04/2021 21.46, will wray via Liaison wrote:
> This proposal is poorly motivated and deleterious, IMO.
> Unfortunately it is popular / populist so acceptance looks likely.
> That'll be a mistake as it is an actively damaging change.
> Let me explain (at least so I can say "told you so").
> The R5 revision states that EWG requested more motivation.
> The new R5 motivation simply rearranges previous prose into
> a sequence of bullet-points with (Clouseau style) conclusion:
> • Therefore, we should let operator[] take multiple arguments.
> Please Non! What's the problem?
> In C, the subscript operator returns a subobject lvalue. Good.
> In C++, subscript operator overload should follow suit - it does,
> mostly, but vector-of-bool returns proxy-reference instead -
> this is now generally accepted as a very nasty design flaw.

The problem is not that [] on some type returns a proxy-reference;
the problem is that it's a specialization of std::vector that
does so. (There are guarantees on std::vector<T> for any T
that [] yields a true reference, via the iterator requirements.)

> In C++, multi-indexing is done with paren function-call syntax.
> This is by now well established practice and is semantically fine.
> Multi-indexing is indeed a functional mapping, often partial.
> (P2128 protests parens "carry the wrong semantic implications").
> Paren multi-indexing could just as easily (or more easily) be
> extended to C, as nested C array has natural partial indexing.
> Multi-indexing is most powerful when partial-indexing can return
> a proxy reference, i.e. not necessarily an actual subobject.
> When indexing with parens, devs know to expect a proxy result.

Proxy results are not, per se, troublesome, and there is no
reason why (i...) returning a proxy reference is any better
or worse than [i...] returning a proxy reference.

> So, there is a clear semantic distinction with the status quo:
> [i] means subobject indexing, returning a language lvalue
> (i...) means multi-indexing, possibly partial, returning a proxy

That's not necessarily true. See [valarray.sub] for an example
for operator[] returning proxies, independent of vector<bool>,
that was in C++ since C++98.

> This proposal adds:
> [i...] what does this mean?
> With operator overloading, it could mean anything at all.

Yes, and that's been the situation with operator overloading
since it came to be. << means bit-shift as a built-in,
and means "output sequence" in conjunction with <iostream>.

  | means bitwise-or as a built-in, and means view
concatenation with [ranges].

  x = {i...}
only works for overloaded operators, as does

Neither have a built-in meaning. I'm not seeing how that
makes an argument in favor or against [i...] .

> The intended "motivating" use case conflates subobject vs proxy;
> an infamous gotcha and dangerous pitfall.
> Devs will have to carefully check the semantics.

I think C++ is sufficiently powerful in its abstractions that
returning a proxy vs. returning a language lvalue is not an
important distinction to make for day-to-day usage.
If you believe otherwise, please show some examples.

> P2128 is proposing something unprecedented - to add a new
> C++ operator that has no current core language meaning.

No new operator token is proposed; it's just the arity of
the operator that is generalized.

> This might constrain future language evolution, C and C++.

Every language extension constrains further language
evolution one way or another. If we want to make
progress beyond stating tautologies, some more specifics
might be helpful.

> A more useful core language meaning might be:
> int a[4] = {1,2,3,4};
> a[1,2] = a[2,1]; // swap
> a[1,2,3,0] = a[0,1,2,3]; // it's a rotate !

So far, C++ has shied away from adding more power and beauty to
C-style built-in arrays, instead offering class abstractions such
as std::vector, std::array, or std::valarray. I think that is/was
a good decision.

The particular semantics proposed above seem relatively
uninteresting to me for putting them into an overloaded
operator (as opposed to a named function).

> Of course, this _could_ be done with the proposed C++ overload.

How so? You can't overload on built-in types.

> That's the problem - it _could_ do anything - anything at all.
> It muddies what is currently a clear semantic distinction

The idea that overloaded operators unconditionally need to
mirror their semantics on built-in types never was.


Received on 2021-04-25 15:52:31