C++ Logo


Advanced search

Subject: Re: [std-proposals] Operator for reference casting
From: Jason McKesson (jmckesson_at_[hidden])
Date: 2021-01-16 23:13:09

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

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 list run by std-proposals-owner@lists.isocpp.org

Standard Proposals Archives on Google Groups