1

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.

Community
  • 1
  • 1
LRaiz
  • 617
  • 6
  • 17
  • 1
    Does it work on a different compiler? – Kerrek SB Jun 06 '14 at 22:52
  • I would guess that you're debugging an optimized build? – Mooing Duck Jun 06 '14 at 22:53
  • 5
    [Working as indented](http://ideone.com/4kr3jI) – Kerrek SB Jun 06 '14 at 22:54
  • 2
    I once asked the same question [here](https://stackoverflow.com/questions/4316727/returning-unique-ptr-from-functions?rq=1) and the answer is that both are valid. You shouldn't have to explicitly move it. – Praetorian Jun 06 '14 at 23:05
  • 2
    The code works just fine with Clang 3.5, too. Are you sure you're showing an accurate representation of your problem? – Kerrek SB Jun 06 '14 at 23:09
  • 1
    "internal pointer being held by result becomes nullptr as soon as execution exist Foo constructor" - where is a `Foo` constructor used, other than in the first statement? – aschepler Jun 06 '14 at 23:10
  • 2
    Do you have -std=c++11 turned on? – Howard Hinnant Jun 06 '14 at 23:19
  • 1
    "The same problem.." - *What problem*? You've mentioned what the *debugger* shows. But you never mentioned if the *code works*, nor have you said whether this is a **debug** vs. **release** build. Both your return versions work as-expected on Apple LLVM version 5.1 (clang-503.0.40) (based on LLVM 3.4svn). At least in my corner of the universe. – WhozCraig Jun 06 '14 at 23:30
  • @KerrekSB - I did not try GCC, not quite sure how to check CLang version but I do have the latest version of Xcode - 5.1.1. I believe my representation is accurate, but it is probably difficult to reproduce because I see it working okay in other instance. – LRaiz Jun 07 '14 at 00:30
  • @HowardHinnant -std=c+11 is on – LRaiz Jun 07 '14 at 00:32
  • @aschepler - Foo constructor is not used anywhere else. – LRaiz Jun 07 '14 at 00:32
  • @LRaiz The `return std::move(result);` prevents Named Return Value optimization. So it's important to know whether that optimization is in effect in your build: Do you test this in debug or release mode (as many have already asked)? -- *"Is CLang playing some optimization games"* suggests it's release mode? – dyp Jun 07 '14 at 00:38
  • 1
    @dyp - it is a debug build. I don't know it that is important but I am working not in Xcode but in QtCreator – LRaiz Jun 07 '14 at 00:42
  • 1
    @dyp: Optimization setting won't impact NRVO. But you are correct that wrapping `result` in `std::move` will prevent it. – Howard Hinnant Jun 07 '14 at 01:41
  • 1
    @HowardHinnant Oh, that's interesting! In MVSC2013, NRVO is not performed in a Debug build. – dyp Jun 07 '14 at 12:34
  • I very much disagree with this question being marked as duplicate and my reputation marked down. The previous question asked what kind of constructor copy or move would be invoked and why a code would not compile otherwise. I on the other hand point out that both versions should work, compiler must 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 return statement (even in debug build). My question should be taken seriously, not dismissed as duplicate. – LRaiz Jun 07 '14 at 12:46
  • @dyp: Thanks! I did not know that, and that is very useful information for me. – Howard Hinnant Jun 07 '14 at 17:07
  • 3
    @LRaiz: Please post the smallest complete example you can that produces the symptom. And post the flags used to compile it. – Howard Hinnant Jun 07 '14 at 17:09
  • Sorry, can't reproduce in a small example. – LRaiz Jun 08 '14 at 02:42
  • 1
    @LRaiz that suggests undefined behaviour elsewhere in your code that is manifesting itself as strange behaviour here – M.M Jun 08 '14 at 07:38

0 Answers0