C++ Logo

std-proposals

Advanced search

Re: A new qualifier for "uniquely referable" object

From: Arthur O'Dwyer <arthur.j.odwyer_at_[hidden]>
Date: Thu, 18 Mar 2021 13:31:42 -0400
On Thu, Mar 18, 2021 at 1:00 PM Omer Rosler via Std-Proposals <
std-proposals_at_[hidden]> wrote:

> Proposal idea:
> A keyword (bikeshedding - `unique_referable`) for declaring that there is
> only one way to refer to an object throughout its lifetime.
>

This is very similar to `restrict`/`noalias`, and has the same issues (plus
a few more issues that I think are easily severed).
https://www.lysator.liu.se/c/dmr-on-noalias.html

1. Allow more aggressive copy elision rules:
> ```c++
> pair<non_copyable, int> mrv_function()
> {
> unique_referable non_copyable a = {};
> //fill a
> return {a, 42}; //OK: copy elision is mandatory here
> }
> ```
> The rules to allow this are completely analogous to `prvalue`
> materialization conversion, only we delay materialization for an lvalue.
>

You seem to be assuming that `return {non_copyable{}, 42}` works with
std::pair today. It does not, because std::pair is not an aggregate.
https://godbolt.org/z/x6fWEW
So forget #1; your proposal doesn't solve it.



> 2. Prevent dangling references.
> ```c++
> int& get_dangling()
> {
> unique_referable int a = 42;
> return a; //Compile error: Can't bind reference to uniquely referable
> object
> }
> ```
>

This returns a dangling reference even without the extra keyword. This will
become a hard error (again, without the need for any extra keyword) after
P2266 "Simpler implicit move" lands. We just had an EWG telecon on P2266
yesterday. My impression (as the paper's author) is that we will likely see
P2266 adopted for C++23... *contingent* on someone producing a reference
implementation so that people can see what existing code is broken by it.
(Interested in producing such an implementation? Send me an email. I am
attempting to coordinate, but not implement, as implementation is far above
my pay grade.)
https://wg21.link/p2266 (R1 is in the March mailing, which isn't out quite
yet)
So forget #2; it's already solved in the core language.


> 3. Help compiler escape analysis:
> ```c++
> extern void foo( unique_referable int& ref);
> int bar()
> {
> unique_referable int a = 42;
> foo(a); // OK: in `foo`, the unique way to refer to the underlying
> object is through `ref`
> return a; //Mandatory copy elision
> }
> ```
>

When I use my fingers to count up `a` and `ref`, I end up with two fingers.
That's not very "unique," is it?
(This is the main problem with `restrict`/`noalias`.)
Copy elision already kicks in here, today, without any extra keywords;
there's no need to make it "mandatory."
Furthermore, URVO *explicitly cannot apply* to register types such as `int`
(which are trivially copyable and suitably small), and so NRVO (copy
elision) can't be made "mandatory" to apply to them either. The next time
you present this example, use a non-trivial type such as `std::string`, not
`int`.

And then I still don't see why you need an extra keyword just to trigger
copy elision. There are existing proposals for "mandatory NRVO," such as
Anton Zhilin's P2025 "Guaranteed copy elision for return variables."
http://www.open-std.org/jtc1/sc22/wg21/docs/papers/2020/p2025r1.html

4. Allow destructive moves
> ```c++
> struct A
> {
> A( unique_referable A&&a) = default;
> };
> ```
> This move is always destructive because `a` is the unique way to access
> the underlying object and is an rvalue-reference, therefore this move is
> always destructive.
>

No, that's not what "destructive" means at all. "Destructive" means "the
operation is responsible for calling the destructor, i.e., the operation *takes
ownership* of the source *object* itself." You should watch this talk,
whose second half surveys the existing literature on "destructive move and
relocatability."
https://www.youtube.com/watch?v=SGdfPextuAU
You can't have a reference type that says "give me a reference to your
object, and btw I'm going to destroy it," unless the caller can somehow
tell that this is going to happen and therefore refrain from
double-destroying the object itself. And of course this might be
path-dependent. So you end up having to make the compiler do control-flow
analysis, and add boolean guard variables, and so on. It's a huge mess.
Consult P2025 for some interesting examples.

HTH,
Arthur

Received on 2021-03-18 12:31:56