With a code like
```
import core.lifetime : forward;
import core.stdc.stdio;
import std.algorithm : move;
struct Value(T) {
private T storage;
this()(auto ref T val) {
storage = forward!val;
}
ref inout(T) get() inout {
return storage;
}
}
Value!T value(T)(auto ref T val) {
return Value!T(forward!val);
}
auto ref unwrap(EX)(auto ref EX res) {
printf("unwrap()\n");
return res.get();
}
struct Foo {
int n;
@disable this(this);
~this() { printf("~this(%d)\n", n); }
}
auto gen() {
Foo f;
f.n = 42;
return value(f.move());
}
void main() {
Foo f;
f = gen().unwrap.move;
}
```
`unwrap` accepts copy of non copyable `Value!Foo` that results in this output:
```
~this(0)
~this(0)
~this(0)
unwrap()
~this(42) <- this is a copy (that shouldn't exist) being destroyed
~this(0)
~this(42)
```
But it should't compile as output from `gen()` is rvalue.
`__traits(isRef, res)` yields false in `unwrap` as expected, same with `isCopyable!(Value!Foo)`.
Or is NRVO working even when return value is passed as a value to the `unwrap`? But then why wouldn't unwrap.move() cause the payload to be reset?
And is it ok to ref being returned from `unwrap` when it's from local value parameter?
Comment #1 by chalucha — 2021-11-09T19:37:52Z
I've found possible workaround.
```
auto ref unwrap(EX)(auto ref EX res) {
static if (__traits(isRef, res)) {
printf("unwrap(ref)\n");
return res.get();
} else {
printf("unwrap(val)\n");
auto ret = res.get().move(); // < NRVO
return ret;
}
}
```
Then with `Foo f = gen(42).unwrap;`
It otputs:
```
~this(0)
~this(0)
~this(0)
unwrap(val)
~this(0)
~this(42)
```
But I'm still puzzled about what's wrong with the original one.
Comment #2 by mail — 2021-11-12T14:17:25Z
As have I commented on the newsgroup https://forum.dlang.org/post/[email protected]
"
It doesn't copy. It just destructs the same object twice... Ouch.
Change the destructor to ~this() { printf("~this(%d)\n", n); n = 12; } and you'll see.
"
Comment #3 by robert.schadek — 2024-12-13T19:19:16Z