I have a class Foo - nothing special about it. A method of another class is returning a unique_ptr to a Foo. After a day of banging my head against my monitor I reduced the problem to the following:
std::unique_ptr<Foo> Other::method(float arg) {
Foo *ptr = new Foo(arg);
std::unique_ptr<Foo> result;
result.reset(ptr);
.....
return result; // option 1 - does not work
return std::move(result); // option 2 would work
}
If option 2 return is commented out and using CLang & LLDB on Mac I observe that as soon as the reset method is called, result starts thinking that it owns a nullptr. The same problem is observed when definition of result is simplified to a single line:
std::unique_ptr<Foo> result(new Foo(arg));
Debugger shows that internal pointer being held by result becomes nullptr as soon as execution exits the Foo constructor.
On the other hand things start showing reasonable behavior as soon as I comment out option 1 of return statement and replace it with option 2. This is despite the fact that there are dozens of code lines between initialization and return.
All this is very surprising. Especially considering that similar code works in another situation without using std::move(). Is CLang playing some optimization games and has a bug or I don't understand something?
I believe this question is different from previously asked one The previous question asked what kind of constructor copy or move would be invoked by return statement and why a code would not compile without explicit move. I on the other hand point out that both versions should work, c++11 compiler should figure out to use move constructor by itself. Worse, CLang in this case does premature NRVO and thus invalidates dozens of lines of code between unique_ptr initialization and offending return statement (even in debug build). Instead of compiler playing such games I'd prefer it simply refuse to compile in the absence of explicit invocation of std::move(). That would save me countless hours hunting for a problem.