我已经阅读了关于RVO的Dave Abrahams article和其他一些关于SO的问答(14043609,9293726和10818278)但我还有一个问题。当我编译并运行以下代码时,我得到了这个输出:
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
对我而言似乎是制作副本。如何从函数中传递大对象?请注意,我想返回一个或多个对象而不为其编写显式结构。我使用GCC 4.6.3和-O2。编辑:前两个答案告诉我,我对编译器的期望太高了。我添加了一个行为方式相同的main2,例如打印的地址不同。我想强调的是,动机是大型物体的有效回归。
#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;
std::cout << "Address of v in func\t" << &v << std::endl;
std::cout << "Address of v.data in func\t" << v.data() << std::endl;
return make_tuple(v, a);
}
int main() {
std::vector<int> v;
double a;
std::tie(v, a) = func();
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;
return 0;
}
int main2() {
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;
return 0;
}
答案 0 :(得分:4)
如前所述,有两件事阻止了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;
}
输出:
Address of v in func 0x28fe80
Address of v.data in func 0x962c08
Address of v in main 0x28fe80
Address of v.data in func 0x962c08
9
替代优化是在构造元组时使用移动语义:
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
答案 1 :(得分:3)
由于v
和a
都已在main()
中声明为变量,因此没有副本可以删除。你在这里得到的是复制作业,而不是复制作品。它相当于:
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.
}
答案 2 :(得分:2)
RVO很可能在这里发生,但是你给出的代码中复制省略的唯一机会是将make_tuple(v, a)
的返回值复制到func()
的返回值。
无论是否完成此操作,std::vector
和double
仍会被复制。您只是在func()
中将v
的结果分配给a
和main
。复制省略(和RVO)仅适用于复制/移动构造,而不适用于分配。
在&v
中执行main
时,您只是获取v
第一行中定义的main
对象的地址。当然,这与v
中定义的func
对象不同。
答案 3 :(得分:2)
在您的第一个示例中,数据将复制到作业中:
int main() {
std::vector<int> v;
double 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;
}
答案 4 :(得分:0)
感谢您的回答。我发现蒂莫的answer最有帮助。这就是我如何根据自己的风格调整答案。请注意func
和main
中的重复样板。当然,如果有人知道如何摆脱它会很棒!
#include <iostream>
#include <vector>
#include <tuple>
std::tuple<std::vector<int>, double> func() {
std::tuple<std::vector<int>, double> tp;
std::vector<int> & v = std::get<0>(tp);
double & a = std::get<1>(tp);
v.reserve(100);
for (int k=0;k!=100;k+=1)
v.push_back(k);
a = 5.0;
std::cout << "Address of v in func\t" << &v << std::endl;
std::cout << "Address of v.data in func\t" << v.data() << std::endl;
return tp;
}
int main() {
std::tuple<std::vector<int>, double> 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;
return 0;
}