1
// T is move-only
std::optional<T>&& foo() { 
   T t = MakeT();
   // ~T() invoked here
   return std::move(std::optional<T>(std::move(t)));
}


void bar() {
   auto opt = foo();
   // opt.has_value() is false here
}

Using a C++17-compatible compiler, I have a type T for which the above occurs. I do not understand the properties of T that cause this to occur, or how to force the move.

It seems like t is moved into the std::optional correctly - but moveing the local std::optional (a) resets the std::optional, and (b) fails to call Ts move-assignment operator.

Zach
  • 23
  • 3
  • 2
    `foo` returns a reference to a local unnamed object that goes out of scope and dereferencing which leads to UB. So none of this stuff could work (regardless of `T`). – user7860670 Apr 15 '20 at 17:30
  • @user7860670 Perhaps I misunderstand the meaning of returning an r-value reference (what I assume is indicated by `&&` in the return signature). Is the local unnamed object not **moved** into `opt` here, before going out of scope? – Zach Apr 15 '20 at 17:37
  • @Zach It is moved, but at the point where the move constructor is called, the object that the reference references (the temporary created by `std::optional(std::move(t))`) is already destroyed, so you are trying to use an invalid reference causing undefined behavior, see linked duplicate. Temporaries are generally destroyed at the end of the full-expression they were created in, i.e. here after the return value has been initialized, but before the function returns. – walnut Apr 15 '20 at 17:38
  • It is indeed moved into `opt`. The problem is that it is an invalid reference referencing object that went out of scope at function `foo`. – user7860670 Apr 15 '20 at 17:39
  • You should just return `T` or `std::optional` by-value and with `return t;`. That will automatically move construct `opt` from `t` in either case, because of special move rules for return statements and because of C++17 copy elision. – walnut Apr 15 '20 at 17:43
  • @walnut / @user7860670 - If I can summarize for my own understanding, the order-of-events is: (1) `t` **moved** into temporary (2) r-value reference created from temporary (3) function ends, temporary is destroyed, (4) r-value reference is returned, referencing destroyed temporary – Zach Apr 15 '20 at 17:47
  • 1
    @Zach Correct . – walnut Apr 15 '20 at 17:47
  • @walnut Great! Learnt something today. Thank you. – Zach Apr 15 '20 at 18:04

0 Answers0