C++ 为什么RVO不';这里不会发生什么?

C++ 为什么RVO不';这里不会发生什么?,c++,return-value-optimization,C++,Return Value Optimization,我已经读了大卫·亚伯拉罕的《RVO》和其他一些关于SO的问答,但我仍然有一个问题。当我编译并运行以下代码时,我得到以下输出: Address of v in func 0x7fffac6df620 Address of v.data in func 0x2081010 Address of v in main 0x7fffac6df690 Address of v.data in func 0x20811b0 9 在我看来,这似乎是一个副本。如何将大型对象传

我已经读了大卫·亚伯拉罕的《RVO》和其他一些关于SO的问答,但我仍然有一个问题。当我编译并运行以下代码时,我得到以下输出:

Address of v in func    0x7fffac6df620
Address of v.data in func       0x2081010
Address of v in main    0x7fffac6df690
Address of v.data in func       0x20811b0
9
在我看来,这似乎是一个副本。如何将大型对象传递出函数?请注意,我希望返回一个或多个对象,而不为其编写显式结构。我将GCC4.6.3与-O2一起使用。编辑:前两个答案告诉我,我对编译器的期望太高了。我添加了一个main2,其行为方式相同,例如打印的地址不同。我想强调的是,其动机是有效归还大型物品

#包括
#包括
#包括
std::tuple func(){
std::向量v;
v、 储备(100);
对于(int k=0;k!=100;k+=1)
v、 推回(k);
双a=5.0;

std::cout由于
v
a
都已在
main()
中声明为变量,因此没有要elide的副本。这里得到的是副本赋值,而不是副本构造。它与此等效:

struct Foo {};

Foo foo() { return Foo(); }

int main()
{
  Foo f1;
  f1 = foo();  // no copy hence f1 is distinct from object returned
  Foo f2 = foo(); // We can get RVO here, returned object can be f2.
}

RVO很可能发生在这里,但在您给出的代码中,唯一的复制省略机会是将
make\u tuple(v,a)
的返回值复制到
func()的返回值中

无论是否执行此操作,都会复制
std::vector
double
。您只是将
func()
的结果赋值给
main
中的
v
a
。复制省略(和RVO)仅适用于复制/移动构造,而不是赋值


当您在
main
中执行
&v
操作时,您只需获得
v
对象的地址,该对象在
main
的第一行中定义。当然,这与
func
中定义的
v
对象不同,正如前面所述,有两种情况会阻止RVO。该函数不会返回
v
,而是从
v
a
构造的元组。在主函数
v
中,也分配元组,而不是从返回值构造元组

要获得所需的内容,可以直接使用元组,而无需额外的向量对象:

#include <iostream>
#include <vector>
#include <tuple>

std::tuple<std::vector<int>, double> func() {
  std::tuple<std::vector<int>, double> t;
  get<0>(t).reserve(100);
  for (int k=0;k!=100;k+=1)
    get<0>(t).push_back(k);

  get<1>(t) = 5.0;
  std::cout << "Address of v in func\t" << &get<0>(t) << std::endl;
  std::cout << "Address of v.data in func\t" << get<0>(t).data() << std::endl;
  return t;
}

int main()
{
  std::tuple<std::vector<int>, double> t = func();
  std::cout << "Address of v in main\t" << &get<0>(t) << std::endl;
  std::cout << "Address of v.data in func\t" << get<0>(t).data() << std::endl;
  std::cout << get<0>(t)[9] << std::endl;

    return 0;
}
另一种优化方法是在构造元组时使用移动语义:

 return make_tuple(std::move(v), a);
在这种情况下,至少可以避免复制向量的内部缓冲区:

Address of v in func    0x28fdd4
Address of v.data in func       0xa72c08
Address of v in main    0x28fe64
Address of v.data in func       0xa72c08
9

在第一个示例中,数据复制到分配中:

int main() {
  std::vector<int> v;
  double a;
  std::tie(v, a) = func();
intmain(){
std::向量v;
双a;
std::tie(v,a)=func();
在第二个示例中,创建元组时仍会复制数据。此修改后的示例显示RVO确实发生了:

#include <iostream>
#include <vector>
#include <tuple>

std::tuple<std::vector<int>, double> func() {
  std::vector<int> v;
  v.reserve(100);
  for (int k=0;k!=100;k+=1)
    v.push_back(k);

  double a = 5.0;

  const auto ret = make_tuple(v, a);
  const auto &v1 = std::get<0>(ret);

  std::cout << "Address of v in func\t" << &v1 << std::endl;
  std::cout << "Address of v.data in func\t" << v1.data() << std::endl;

  return ret;
}

int main() {
  auto tp = func();
  std::vector<int> & v = std::get<0>(tp);
  double & a = std::get<1>(tp);
  std::cout << "Address of v in main\t" << &v << std::endl;
  std::cout << "Address of v.data in func\t" << v.data() << std::endl;
  std::cout << v[9] << std::endl;

  (void)a;
}
#包括
#包括
#包括
std::tuple func(){
std::向量v;
v、 储备(100);
对于(int k=0;k!=100;k+=1)
v、 推回(k);
双a=5.0;
const auto ret=make_tuple(v,a);
const auto&v1=std::get(ret);

std::cout谢谢你的回答。我发现Timo的答案非常有用。这就是我如何根据自己的风格修改答案的方法。注意
func
main
中重复的样板文件。当然,如果有人知道如何摆脱它,那就太好了

#包括
#包括
#包括
std::tuple func(){
std::tuple-tp;
std::vector&v=std::get(tp);
double&a=std::get(tp);
v、 储备(100);
对于(int k=0;k!=100;k+=1)
v、 推回(k);
a=5.0;

我是否可以添加第二个应该与您的代码等效的示例。但是,它仍然没有使用RVO。我是否遗漏了一些内容?
#include <iostream>
#include <vector>
#include <tuple>

std::tuple<std::vector<int>, double> func() {
  std::vector<int> v;
  v.reserve(100);
  for (int k=0;k!=100;k+=1)
    v.push_back(k);

  double a = 5.0;

  const auto ret = make_tuple(v, a);
  const auto &v1 = std::get<0>(ret);

  std::cout << "Address of v in func\t" << &v1 << std::endl;
  std::cout << "Address of v.data in func\t" << v1.data() << std::endl;

  return ret;
}

int main() {
  auto tp = func();
  std::vector<int> & v = std::get<0>(tp);
  double & a = std::get<1>(tp);
  std::cout << "Address of v in main\t" << &v << std::endl;
  std::cout << "Address of v.data in func\t" << v.data() << std::endl;
  std::cout << v[9] << std::endl;

  (void)a;
}