C++ Logo

std-proposals

Advanced search

Re: [std-proposals] User defined decay operator

From: Jeremy Rifkin <rifkin.jer_at_[hidden]>
Date: Thu, 20 Mar 2025 21:58:15 -0600
> If I understand this correctly then this new decay operator will override
glvalue-to-prvalue conversions

That would cause tons of problems. It would effectively make your class no
longer able to be used in most expressions.

> if `temp` is still the expression `a * b` then `+= temp` is an expression
too that needs to be evaluated for every `x`.

Correct, the expression template has to be deliberately evaluated at some
point in your code. You seem to want to make this evaluation happen
implicitly, which is a bad idea for all the same reasons array to pointer
decay is bad. That would be subtle, surprising, and would interfere with
most normal uses of expression templates (i.e., actually building the
expression template).

> > What would it return though?
> MyTuple<int&, int&> minmax(int& a, int& b);

So you'd create an entirely new tuple implementation just for this? That
can't be good.

Cheers,
Jeremy


On Thu, Mar 20, 2025 at 6:39 PM Marcin Jaczewski <
marcinjaczewski86_at_[hidden]> wrote:

> pt., 21 mar 2025 o 01:01 Jeremy Rifkin <rifkin.jer_at_[hidden]> napisał(a):
> >
> > > Probably only in couple of relevant places of usage array-to-pointer
> we should
> > put this user defined decay operator.
> >
> > It may be of interest to consider that nowhere in the standard does it
> directly say `auto x = arr;` undergoes "decay" or array-to-pointer
> conversion. This is a consequence of conversion sequences and there not
> being a glvalue-to-prvalue conversion for array types. The problem with
> what you want to propose is that class types *do* have glvalue-to-prvalue
> conversions.
> >
>
> If I understand this correctly then this new decay operator will
> override glvalue-to-prvalue conversions.
> overall both happens in majority places next to each other is standard
> text.
>
> > > > What you're describing is a bug in the implementation of that
> expression template. I'm also not sure how decay is supposed to help that.
> > > Its not a bug,
> >
> > The bug is trying to store a reference in the expression template and
> letting the user stumble into this footgun. It's also still not clear to me
> how decay is supposed to help here.
> >
> > > Now we have by orders of magnitude more operations
> >
> > I'm not following this argument
>
> if `temp` is still the expression `a * b` then `+= temp` is an
> expression too that needs to be evaluated for every `x`.
> We could write this as equivalent to more explicit code:
>
> ```
> for (auto x : tab) x = eval(x + a * b);
> ```
>
> And this needs multiple `a * b` each iteration. compare this to:
>
> ```
> for (auto x : tab) x = eval(x + t);
> ```
>
> where we need only add two matrices each iteration.
>
> >
> > > This would be used in `std::minmax2` or `std2::` or your own classes
> >
> > What would it return though?
> >
>
> ```
> MyTuple<int&, int&> minmax(int& a, int& b);
> ```
> but after `auto x = minmax(a, b)`
> type is `MyTuple<int, int> x` as it decayed.
>
>
> > Cheers,
> > Jeremy
> >
> >
> > On Thu, Mar 20, 2025 at 5:25 PM Marcin Jaczewski <
> marcinjaczewski86_at_[hidden]> wrote:
> >>
> >> czw., 20 mar 2025 o 23:40 Jeremy Rifkin via Std-Proposals
> >> <std-proposals_at_[hidden]> napisał(a):
> >> >
> >> > Hi,
> >> >
> >> > This idea would benefit from a lot of elaboration on exactly how this
> proposed decay is supposed to happen and when.
> >> >
> >>
> >> Rule of thumb would be in same place where `int[5]` would decay to
> `int*`.
> >> And this new operator would control `std::decay_t` result too.
> >>
> >> In my previous email I said that it should behave similar to
> >> array-to-pointer conversion
> >> but after checking standard text it has a bit more broader usage that
> >> I would like.
> >> Adding this to the user-defined conversion step would be asking for
> problems.
> >> Probably only in couple of relevant places of usage array-to-pointer we
> should
> >> put this user defined decay operator.
> >>
> >> > > Some consider this "bad feature" but for me this is more is "bad
> >> > implementation" of "good feature".
> >> >
> >> > It's broadly considered a bad feature because implicit conversions
> can be subtle and surprising. This does not change with implementation.
> >> >
> >> > > I think that many user defined types could benefit from this
> functionality.
> >> > > Proxy objects
> >> >
> >> > Can you clarify how you think some sort of user-specified decay helps
> the proxy object case? That already works fine as-is.
> >>
> >> Problem with proxy is when passed as value it still behaves as
> reference type.
> >> This means it can dangle or update spruce unexpectedly.
> >> And there is no generic way to prevent it, if we could use decay then
> it become
> >> near generic operation of getting rid of references and proxy objects.
> >> Consider this code:
> >>
> >> ```
> >> assert(x != y);
> >> auto a = x;
> >> a = y;
> >> assert(x != y); //ups... proxy
> >>
> >> auto b = temp()[10]; //only proxy is dangling
> >> ```
> >>
> >> and:
> >>
> >> ```
> >> assert(x != y);
> >> auto& a = x;
> >> a = y;
> >> assert(x == y); //ok for majority of types
> >>
> >> auto& b = temp()[10]; //always dangling
> >> ```
> >>
> >> with this decay proxies will have limited range it will unexpectedly
> >> propagate as values,
> >> and only by using `&` you can expand its reach and this has benefits
> >> for being more visible.
> >>
> >> >
> >> > > Expression templates
> >> >
> >> > What you're describing is a bug in the implementation of that
> expression template. I'm also not sure how decay is supposed to help that.
> >>
> >> Its not a bug, alternatively it would be forced to copy all temporary
> >> arguments even if it will be discarded at the end of expression.
> >> Another problem is evaluation as same expression can be reused in
> >> couple of places:
> >>
> >> ```
> >> auto temp = a * b;
> >> for (auto x : tab) x += temp;
> >> ```
> >>
> >> Now we have by orders of magnitude more operations. If expressio can
> >> decay to a regular matrix then we have only simple
> >> addition in the loop like if we used plain floats.
> >>
> >> >
> >> > > Recent problem discussed on this mailing list with ref types
> >> >
> >> > It's not clear to me how decay is supposed to change that, especially
> without breaking code. This is just an unfortunate consequence of the
> design of std::minmax.
> >>
> >> But if my `Tuple` have decay and if I would implement `minmax` using
> >> it then original poster would not have this problem:
> >>
> >> ```
> >> auto& [x, y] = std::minmax(foo, bar);
> >> //x and y are members from `Tuple<Foo&, Bar&>&` pointing to `foo` and
> `bar`
> >>
> >> auto [x, y] = std::minmax(foo, bar);
> >> //x and y are value members from `Tuple<Foo, Bar>`
> >> ```
> >>
> >> And I do not ask for changing `std::tuple` as this would be indead
> >> breaking change,
> >> This would be used in `std::minmax2` or `std2::` or your own classes.
> >>
> >> >
> >> > Cheers,
> >> > Jeremy
> >> >
> >> >
> >> > On Thu, Mar 20, 2025 at 10:16 AM Marcin Jaczewski via Std-Proposals <
> std-proposals_at_[hidden]> wrote:
> >> >>
> >> >> Right now the only type that undergoes decaying is C array (aside of
> >> >> refs or bit fields).
> >> >> Some consider this "bad feature" but for me this is more is "bad
> >> >> implementation" of "good feature".
> >> >>
> >> >> I think that many user defined types could benefit from this
> functionality.
> >> >>
> >> >> Proxy objects:
> >> >> ```
> >> >> std::vector<bool> ff = { false, false };
> >> >> for (auto b : ff)
> >> >> {
> >> >> b = true;
> >> >> }
> >> >> assert(ff[0] == true);
> >> >> ```
> >> >>
> >> >> Expression templates:
> >> >> ```
> >> >> auto matrix = Matrix2x2{1, 2, 3, 4}
> >> >> auto mul = matrix * Vector2{3, 4};
> >> >> Vector2 v = mul + Vector2{1, 1}; //UB because `mul` is expression and
> >> >> dangle temporal vector
> >> >> ```
> >> >>
> >> >> Recent problem discussed on this mailing list with ref types:
> >> >>
> >> >> ```
> >> >> auto [a, b] = std::minmax(foo, 10);
> >> >> auto c = a; // UB if `a` refer to `10`
> >> >> ```
> >> >>
> >> >> Another could be a safer version of C array that is not copyable like
> >> >> `std::array`
> >> >> and instead of decay to pointer, it will decay to `std::span`.
> >> >>
> >> >> And finally if someone desires to ban "Almost Always Auto" as it
> could
> >> >> be useful for
> >> >> very heavy types for unexpected copies in generic code (by setting
> `= delete`).
> >> >>
> >> >> ```
> >> >> auto x = a; //error
> >> >> auto& x = a; //ok, no decay
> >> >> NestedVector x = a; //ok
> >> >> auto x = NestedVector{ a }; //ok
> >> >> ```
> >> >>
> >> >> Code that enable it would look like:
> >> >>
> >> >> ```
> >> >> struct Foo
> >> >> {
> >> >> Bar operator auto()
> >> >> {
> >> >> return Bar{};
> >> >> }
> >> >> };
> >> >>
> >> >> struct auto_ptr //pre C++11 unique_ptr
> >> >> {
> >> >> //nothing change but in places where it could be used diagnostic
> is raised
> >> >> [[obsolete]] auto_ptr operator auto() = default;
> >> >> };
> >> >>
> >> >> struct MyArray
> >> >> {
> >> >> //ban decay from temp objects like return from functions.
> >> >> std::span<int> operator auto() && = deleted;
> >> >> std::span<int> operator auto();
> >> >> };
> >> >>
> >> >> template<typename T1, typename T2>
> >> >> struct MyTuple
> >> >> {
> >> >> T1 t1;
> >> >> T2 t2;
> >> >> //decay propagate to members, making ref tuple safer to use if
> >> >> returned form `std::minmax`
> >> >> MyTuple<std::decay_t<T1>, std::decay_t<T2>> operator auto() {
> >> >> return { t1, t2 }; }
> >> >> };
> >> >> ```
> >> >>
> >> >> When a type has multiple `operator auto` then all versions need to
> >> >> return the same type.
> >> >> Additionally, the returned type need not have a decay operator (or
> its
> >> >> `=default`).
> >> >>
> >> >> Overall this make `auto x =` generally more safe to use and more
> regular.
> >> >> --
> >> >> Std-Proposals mailing list
> >> >> Std-Proposals_at_[hidden]
> >> >> https://lists.isocpp.org/mailman/listinfo.cgi/std-proposals
> >> >
> >> > --
> >> > Std-Proposals mailing list
> >> > Std-Proposals_at_[hidden]
> >> > https://lists.isocpp.org/mailman/listinfo.cgi/std-proposals
>

Received on 2025-03-21 03:58:30