15

Does Visual C++ not perform return-value optimization?

#include <cstdio>
struct Foo { ~Foo() { printf("Destructing...\n"); } };
Foo foo() { return Foo(); }
int main() { foo(); }

I compile and run it:

cl /O2 test.cpp
test.exe

And it prints:

Destructing...
Destructing...

Why is it not performing RVO?

user541686
  • 205,094
  • 128
  • 528
  • 886
  • 4
    You might want to ask Microsoft, rather than StackOverflow... – David Rodríguez - dribeas Jul 30 '12 at 22:07
  • 1
    @DavidRodríguez-dribeas: Well [Microsoft's documentation](http://msdn.microsoft.com/en-us/library/8f8h5cxt.aspx) says they can perform *named* RVO (which is much more difficult), but I was neither able to get NRVO working, nor simple RVO. So I'm feeling I'm doing something wrong here, since if they didn't support it they wouldn't have mentioned it (hopefully...) – user541686 Jul 30 '12 at 22:08
  • @Mehrdad: Do you have a link to the doc from Microsoft? – Chris Dargis Jul 30 '12 at 22:11
  • It could be, perhaps, that the compiler is calling the print statement twice, but is still only creating one object. – Chris Dargis Jul 30 '12 at 22:18
  • @DougRamsey: lol, no, that's impossible and wouldn't make any sense. James knew the reason -- I accidentally told him his solution doesn't work (because I tested it on a debug build by accident) and then he removed the comment. But it was correct. Now I'm just waiting for him to post it as an answer... it's really weird. :) – user541686 Jul 30 '12 at 22:19
  • 1
    I retracted my comment because I had an alternative hypothesis that turned out to be incorrect (I thought a move construction might be occurring instead of RVO, but that is not the case). I would recommend opening a bug on [Microsoft Connect](http://connect.microsoft.com/VisualStudio), if the issue is important to you. Since the issue only appears to affect class types that have no nontrivial constructors, I'm not sure that it's a huge problem (and since RVO is an optional optimization, it's not a conformance issue), but it's still worth reporting. – James McNellis Jul 30 '12 at 22:30
  • @JamesMcNellis: Thanks for the info. I just tried reporting the bug but when I download the extension, it says the manifest is invalid. I'm not a fan of submitting bug reports to MS just because it's so tedious... :\ – user541686 Jul 30 '12 at 22:33
  • @Mehrdad: It should be fairly painless and can be done without any plugins. The direct link to the form is https://connect.microsoft.com/VisualStudio/feedback/CreateFeedback.aspx. Only a Live account is needed. I've never used the feedback tool, so I can't comment on that. – James McNellis Jul 30 '12 at 22:35
  • 1
    @JamesMcNellis: Oh awesome, thanks! https://connect.microsoft.com/VisualStudio/feedback/details/756190/visual-c-return-value-optimization-inhibited-by-trivial-constructor – user541686 Jul 30 '12 at 22:39

1 Answers1

16

When I test with this:

#include <iostream>
struct Foo { 
    Foo(Foo const &r) { std::cout << "Copying...\n"; }
    ~Foo() { std::cout << "Destructing...\n"; }
    Foo() {}
};

Foo foo() { return Foo(); }

int main() { Foo f = foo(); }

...the output I get is:

Destructing...

No invocation of the copy constructor, and only one of the destructor.

Jerry Coffin
  • 476,176
  • 80
  • 629
  • 1,111
  • 1
    Hmmm... would you mind explaining what causes the difference between this code and my code? – user541686 Jul 30 '12 at 22:20
  • 6
    @Mehrdad: Somehow, the compiler-generated default constructor seems to inhibit [N]RVO. As soon as you add a default ctor of your own, you get working [N]RVO (e.g., if you add and call: `Foo bar() { Foo f; return f; }`, you just get one more destructor invcation, so NRVO works as well). – Jerry Coffin Jul 30 '12 at 22:26
  • @Mehrdad: Sounds like RVO is only considered when a user-defined constructor exists (probably because that's where it originally mattered), and for whatever reason it isn't considered everywhere. – GManNickG Jul 30 '12 at 22:27
  • 6
    The constructor does not necessarily need to be user-declared: causing `Foo` to have a nontrivial default or copy constructor will also "reenable" RVO (e.g., give it a data member of type `std::string`). – James McNellis Jul 30 '12 at 22:28
  • 1
    @JamesMcNellis: :O Where did you learn all this?! I've never seen this information before... – user541686 Jul 30 '12 at 22:29
  • 7
    Bottom line: the compiler only bothers with eliminating copies when there's at least some possibility that copying is expensive enough to be worth eliminating. – Jerry Coffin Jul 30 '12 at 22:31
  • @Ed S.: Also helps to have access to the compiler's source code. – Xander Tulip Jul 31 '12 at 03:00
  • @XanderTulip: I didn't realize he worked for MS on the VS C++ team :D. I suppose that would help a bit. – Ed S. Jul 31 '12 at 04:02