C++ Logo


Advanced search

Re: [std-proposals] Rvalue Parameter : Implicit move and forward

From: Lénárd Szolnoki <cpp_at_[hidden]>
Date: Wed, 21 Dec 2022 08:51:16 +0000

On 21 December 2022 06:45:34 GMT, Frederick Virchanza Gotham via Std-Proposals <std-proposals_at_[hidden]> wrote:
>It surprises me that reference collapsing made it into the C++
>Standard back in 2011.
>One of the obvious complications of reference collapsing is that I
>can't use simple syntax to write a template function that only accepts
>Rvalue arguments, instead I need to use a constraint as on Line #05 :
>01: template<typename T>
>02: optional<T> stored_object;
>04: template<typename T>
>05: requires is_rvalue_reference_v<T&&>
>06: void store(T &&arg)
>07: {
>08: stored_object< remove_cvref_t<T> >.emplace( move(arg) );
>09: }
>Also note on Line #08 that I must use 'move' even though the function
>parameter is an Rvalue reference. I started a thread recently on
>comp.lang.c++ in which I shared my idea for a different way in which
>perfect forwarding could have been implemented:
> https://groups.google.com/g/comp.lang.c++/c/3QhGCWcZfIo
>Since the C++11 standard is already written and finished though, I can
>only make proposals that won't break existing code. So anyway here's
>what my idea is:
>In the definition of a function, and not in any declaration of a
>function, you may specify the '^' symbol before the name of a function
>parameter in order to get implicit use of 'move' and 'forward'. For
>example let's declare a function as follows:
> void RecordString(string &&arg);
>And now let's define it, and notice that I've put a '^' before the
>parameter name:
> void RecordString(string &&^arg)
> {
> some_global_string = arg;
> }
>The above function definition is exactly equivalent to:
> void RecordString(string &&arg)
> {
> some_global_string = move(arg);
> }
>Now let's take an example using a template function that uses
>reference collapsing and 'std::forward' to accept either an Lvalue or
>an Rvalue:
> template<typename T>
> void store(T &&arg)
> {
> stored_object< remove_cvref_t<T> >.emplace( forward<T>(arg) );
> }
>I propose that the use of 'forward' could be made implicit by applying
>'^' to the function parameter:
> template<typename T>
> void store(T &&^arg)
> {
> stored_object< remove_cvref_t<T> >.emplace(arg);
> }
>So essentially what the '^' token does here is that if the parameter
>is an Rvalue reference, then the argument is treated as an Rvalue
>within the body of the function. This goes against the rule of "All
>function arguments are Lvalues" but that rule should have been amended
>anyway back in 2011.

The rule is there for a good reason: It's too easy to reuse things with a name. If there is implicit move from an id expression, then it's easy to accidentally introduce use-after-move bugs. For example with your proposed syntax:

template <typename T>
void foo(T &&^arg)
    baz(arg); // oops, this might be use-after-move

>The '^' token could also be used in range-based 'for' loops:
> for ( auto ^e : some_container ) DoSomethingWithContainerElement(e);

I think you are mixing a few different matters here:
1. Template type deduction for T&&
There might be a room for an alternate syntax with different deduction rules, like your T&&^.

2. Reference collapsing
This is a more generic language feature, not limited to forwarding references. I don't think there is room for messing with it without breaking too many things. There can be different collapsing rules with &&^ though.

3. Value category of id expressions
Don't mess with this.


Received on 2022-12-21 08:51:21