C++ Logo


Advanced search

Re: [wg14/wg21 liaison] Multidimensional subscript operator

From: will wray <wjwray_at_[hidden]>
Date: Sun, 25 Apr 2021 18:31:26 -0400
Thanks for the responses Jens.

(A note that, of course, the issue doesn't come up in C -
 so drifting off topic for a Liaison discussion....
 Lack of language reference forces explicit pointer use
 or, implicitly decayed pointer, with all its peril.)

> The problem is not that [] on some type returns a proxy-reference

Agree. It's about expectations - without having to check contracts.
Nothing is broken now. This proposal will break expectations.
(i...)-indexing is expected to return a proxy,
[i]-indexing is expected to return lvalue.
Let's not mess with that.

> returning a proxy vs. returning a language lvalue is not an
> important distinction to make for day-to-day usage.

It shouldn't be.
And it generally isn't, until it is, and then it's a hard-to-find bug.

> If you believe otherwise, please show some examples.

There isn't a problem with direct use, a[i] = value
(I think... hard to be sure nowadays.)
The issue is generic passing of the returned type.
("generic" appears 10 times in P2128, as motivation).
decltype(auto) is needed to deal with lvalue or proxy.
Nobody uses decltype(auto) everywhere. Or, few know to:

    auto e = a[i]; // incorrect for lvalue
                   // - correct for proxy

    auto& e = a[i]; // retains lvalueness
                    // fails for proxy rvalue

    auto&& e = a[i]; // retains lvalueness
                     // suboptimal for proxy

    decltype(auto) e = a[i]; // recommended?

> x = {i...}
> only works for overloaded operators

and for aggregates, right (this is Liaison list, remember).

On Sun, Apr 25, 2021 at 4:52 PM Jens Maurer <Jens.Maurer_at_[hidden]> wrote:

> 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
> x[{i...}]
> 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.
> Jens

Received on 2021-04-25 17:31:42