views:

305

answers:

4

From the following code, If RVO has happened, I expect to see the 2 addresses pointing to the same location, however this is not the case (my compiler is MS VC9.0)

#include <iostream>
#include <string>

std::string foo(std::string& s)
{
   std::cout << "address: " << (unsigned int)(&s) << std::endl;
   return s;
}

int main()
{
   std::string base = "abc";
   const std::string& s = foo(base);
   std::cout << "address: " << (unsigned int)(&s) << std::endl;
   std::cout << s << std::endl;
   return 0;
}

Under what conditions should RVO be happening?

btw, I'm basing my question on the following discussion: http://cpp-next.com/archive/2009/08/want-speed-pass-by-value/

+2  A: 

You seem to misunderstand RVO, try this example (actually NRVO):

std::string foo(const char* const s)
{
    std::string out(s);
    std::cout << "address: " << (void*)(&out) << std::endl;
    return out;
}

int main()
{
   std::string s = foo("abc");
   std::cout << "address: " << (void*)(&s) << std::endl;
}
Georg Fritzsche
I think you're wrong: http://codepad.org/BuTRF0Ra
dman
Did you turn on optimizations etc. (*Project Properties - C++ - Optimization - O2 or higher*)? RVO is completely non-binding for the compiler by the way.
Georg Fritzsche
aaah I see, so technically, its something like inline where the compiler can decide when and where it wants to optimize.
dman
Yes, basically - it is a common optimization implemented by most compilers, but not required by the standard.
Georg Fritzsche
+4  A: 

I don't know the full conditions, but I believe the fact that you are returning a parameter and not an instance created in the function is causing the problem in your example.

For me, the following showed the same address for both:

#include <iostream>
#include <string>

std::string foo()
{
   std::string s("rvo!");
   std::cout << "address: " << (void *)(&s) << std::endl;
   return s;
}

int main()
{
   const std::string s = foo();
   std::cout << "address: " << (void *)(&s) << std::endl;
   std::cout << s << std::endl;
   return 0;
}

Follow up to darid's comment

The codepad about page documents that for it use's the -fno-elide-constructors for C++. The documentation for this option form the g++ man page state:

The C++ standard allows an implementation to omit creating a temporary which is only used to initialize another object of the same type. Specifying this option disables that optimization, and forces G++ to call the copy constructor in all cases.

On my machine, compiling with -fno-elide-constructors prevents RVO, but compiling without allows it.

R Samuel Klatchko
I think you're wrong: http://codepad.org/j9G52Abp
dman
@darid - I've added to my answer to rebut the fact that codepad does not exhibit RVO.
R Samuel Klatchko
+4  A: 

The correct answer is "whenever the compiler pleases". Such behavior is not mandated (but is allowed) by the standard, and the exact conditions where it kicks in varies from compiler to compiler and version to version.

As a general rule, the compiler is smarter than you, and working in your best interest. Do not question it.

rvalue references in C++0x are sort of a manual version of RVO.

Edit: Looking closer at your code, you're definately misunderstanding RVO. Because your parameter is a reference, there is no way the return value of the function could have the same address.

Terry Mahaffey
+4  A: 

RVO generally applies when you return an unnamed temporary, but not if you return a previously created object.

std::string foo() {
  return std::string("hello world"); // RVO
}

std::string foo() {
  std::string str("hello world");
  bar();
  return str; // Not RVO
}

std::string foo(std::string str) {
  return str; // Not RVO
}

A more general version is NRVO (Named return value optimization), which also works on named variables.

std::string foo() {
  std::string str("hello world");
  bar();
  return str; // NRVO
}

std::string foo(std::string str) {
  return str; // Not NRVO, as far as I know. The string is constructed outside the function itself, and that construction may be elided by the compiler for other reasons.
}

std::string foo(std::string str) {
  std::string ret;
  swap(ret, str);
  return str; // NRVO. We're returning the named variable created in the function
}
jalf