You know, maybe forwarding the arguments is the wrong direction. Maybe there should have been the dual mechanism of back-propagating the ultimate destination of the object all the way back to its constructor instead.
You know, maybe forwarding the arguments is the wrong direction. Maybe there should have been the dual mechanism of back-propagating the ultimate destination of the object all the way back to its constructor instead.
Production code in Rust is littered with imperformant `.clone()`s but at least I can see where they're happening to ponder a better way.
You might say "Big deal, it's just a tiny loss of performance to create a value and then copy it into its final place. Unlike C++ this is guaranteed to only do a copy of bytes, no complex code like a copy ctor. Who cares, especially if it's only noticeable in debug builds?" But it's not just a problem of performance. If it's `Box::new([0_u8; 10 * 1024 * 1024])`, then that 10 MiB array created in the caller's stack can end up blowing the caller's stack.
Rust did actually try to add emplace style APIs and a dedicated operator. It would've looked like `vec.place_back() <- Widget::new();` and it would've guaranteed that the generated code did not create a copy in the caller frame. It was never stabilized and instead eventually removed, because it was still not reliable enough to provide that guarantee after all.
https://github.com/rust-lang/rfcs/blob/master/text/1228-plac...
https://doc.rust-lang.org/1.26.0/std/vec/struct.Vec.html#met...
https://github.com/rust-lang/rust/issues/27779#issuecomment-...
Box::<u8>::new_zeroed_slice(10 * 1024 * 1024) says we want 10MiB of zero bytes as a MaybeUninit inside a box, we can then (since these are just bytes in our example) assume_init() since that's valid for our type although in the real world probably we'd actually store some actual data in the memory we've allocated - but it doesn't go on the stack.
If we're overwriting it all anyway there's also an adjacent set of uninit functions to skip the zero step, although of course the OS might be zeroing the page anyway.
What I'm thinking of would be
```c++ struct MyBigClass { /* lots of members */ };
MyBigClass makeABigOne();
auto main() -> int { // We still need to construct a return slot here, even though we don't use the value, right? makeABigOne(); } ```
Perhaps this is tangential, but I'm wondering if maybe I'm missing a subtly based on what you mean by "allocated" (in the abstract machine, or in the ABI).
So yes, storage for the (to-be discarded) object must be allocated, and the object is constructed into that storage.
I don’t know enough about the ABI to comment about the last point.
bar foo() {bar baz; ...; return baz;}
bar qux = foo();
'baz' is constructed directly in the space allocated for 'qux'.
Implementation notes: the complex value is never returned but rather the caller passes the address of a space where the return value should be constructed. Inside the function, the compiler notes that baz is returned and allocates it in the return value space.
This optimization is guaranteed to occur. Compilers which don't do this are nonconforming.
NRVO is not guaranteed to occur, only unnamed RVO (`bar foo() { return bar(...) ; }`) is.
I vaguely remember that there are some obscure corner cases when it's won't occur but I can't recall them since I haven't written C++ since 2016.