开发者

When should RVO kick-in?

开发者 https://www.devze.com 2022-12-15 23:35 出处:网络
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 i开发者_StackOverflow社区s MS VC9.0)

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 i开发者_StackOverflow社区s 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/


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 ret; // NRVO. We're returning the named variable created in the function
}


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.


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.


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;
}
0

精彩评论

暂无评论...
验证码 换一张
取 消