C++ Logo

STD-PROPOSALS

Advanced search

Subject: Re: [std-proposals] Operator for reference casting
From: Drew Gross (drew.a.gross_at_[hidden])
Date: 2021-01-17 03:26:42


Some examples of when you don't want to forward a forwarding reference,
might be easy to create bugs if it forwarded automatically:

1) A generic logging wrapper function
```
auto LoggedCall(auto >&& f, auto >&& v) {
    LogValue(v);
    return f(v); // Oops, v was moved from! (Maybe, depending on signature
of LogValue)
}
```
2) A generic concatenate function
```
auto Concatenate(auto >&& c1, auto >&& c2) {
    std::vector<decltype(c1)::value_type> result;
    using std::size;
    result.reserve(size(c1) + size(c2)); // Oops! c1 and c2 were moved from
(OK only if size's signature is weird for c1/c2's type, but you get the
point)
    // ... add c1 and c2 to result ...
    return result;
}
```

On Sat, Jan 16, 2021 at 9:14 PM Jason McKesson via Std-Proposals <
std-proposals_at_[hidden]> wrote:

> On Sat, Jan 16, 2021 at 4:34 PM Bengt Gustafsson via Std-Proposals
> <std-proposals_at_[hidden]> wrote:
> >
> >
> > >> Given that a language feature has more wiggle room we could define two
> > >> different operators with limitations in applicability that can't be
> > >> enforced for a library feature such as std::forward / std::move.
> > >>
> > >> prefix -> is a forwarding operator which can only be applied to
> > >> universal references, i.e. parameters declared with deduced template
> > >> type and a && modifier.
> > > And what of `auto &&t = expr;`? In terms of the rules for how `t` gets
> > > deduced, it is just as much a forwarding reference as a parameter. And
> > > it's just as reasonable to want to forward `t` as you would for any
> > > parameter. And yet, the standard's definition of "is a forwarding
> > > reference" would not include `t`.
> > Practially you would want it to be possible to forward t here, and
> > auto&& is a shortcut for the full template syntax. So yes, it should be
> > included, in contrast with MyType&& x where you would use move (as you
> > know it is a movable variable).
>
> I've been thinking about this for the last few hours, and something
> occurred to me:
>
> We're kinda doing this backwards.
>
> We're looking at `std::forward` as being this large, difficult-to-type
> and not very easy to use wart in our code. And we want to make a
> shorter version of it. But maybe the problem is not at the point of
> use.
>
> Maybe the problem is that a "forwarding reference" is not a special
> construct when it *should be*. After all, if a forwarding reference is
> a special construct, then anytime you use it, we could say that the
> compiler will *automatically* forward from it. And so we no longer
> need to apply *any* syntax at the point of use.
>
> So how do we do that? A forwarding reference parameter only gains
> special forwarding reference status if it's declared like this:
>
> ```
> template<typename T>
> void func(T &&t);
> ```
>
> But we now have a shorthand for that:
>
> ```
> void func(auto &&t);
> ```
>
> So what if we create a syntax specifically for forwarding references?
> Just as a strawman syntax, we'll use this:
>
> ```
> void func(auto >&&t);
> ```
>
> There would be no long-form version of this with a template header; to
> create an auto-forwarding-reference, you *must* use `auto`. And you
> can't slip a cv-qualifier in there either.
>
> In terms of the function declaration, it's the same as the above.
> However, every use of `t` within the function should *automatically*
> do the forwarding gymnastics. No syntax is needed at the point of use
> to forward the expression's value category.
>
> I feel like this is important because I'm really unsure of when I
> *wouldn't* want to forward a usage of a forwarding parameter. And if
> there's a time for that, we can always have a `std::lvalue` function
> or something for it (if you want an rvalue, you'd use `std::move`).
>
> This would work with any usage of `auto` (except maybe in template
> parameters, but probably there too?):
>
> ```
> auto >&&varname = expr;
> auto >&&[x, y] = expr;
> ```
>
> `varname` is an automatic forwarding reference, so all uses of
> `varname` will forward it properly. Uses of `x` and `y` will properly
> forward from the hidden variable. If the hidden variable is a struct
> or array, `x` and `y` will carry the value category of the referenced
> expression as appropriate. Otherwise, it'll return what `get` says to
> return for the value category of the expression.
>
> This cleans up a bunch of code at the point of use of forwarding
> references, thus making them that much easier to use.
>
> > >
> > > I don't think it's reasonable to have language be functional or
> > > non-functional based on nebulous properties like "is a forwarding
> > > reference".
> >
> > I don't understand this, do you mean functional as in "working" or
> > functional as in "pure functional programming"?
>
> I meant "working".
> --
> Std-Proposals mailing list
> Std-Proposals_at_[hidden]
> https://lists.isocpp.org/mailman/listinfo.cgi/std-proposals
>



STD-PROPOSALS list run by std-proposals-owner@lists.isocpp.org

Standard Proposals Archives on Google Groups