我们有一堆值返回函数:
Foo function_1(){
Foo f;
// ...
return f;
}
Bar function_2(){
Bar b;
// ...
return b;
}
Baz function_3(){
Baz b;
// ...
return b;
}
我正在使用它们来创建局部变量的实例化:
void example(){
//...
const auto foo = function_1();
//...
const auto bar = function_2();
//...
const auto baz = function_3();
}
但是,我的队友一直要求我转换我的所有实例以使用&
:
void example(){
//...
const auto& foo = function_1();
//...
const auto& bar = function_2();
//...
const auto& baz = function_3();
}
这样做的理由似乎符合以下问题:
Why not always assign return values to const reference?
我理解auto& x =
和auto x =
要求两个不同的东西,行为可能会因函数的实现而有所不同。
话虽这么说,我在寻找关于价值回归功能选择的差异(如果有的话)的澄清?行为保证是否相同?
如果它是值返回函数,我如何在const auto&
与const auto
之间做出决定? (大概const在这里没关系?)。我在C ++核心指南中找不到任何关于此的建议。我希望这不是一个有问题的问题。
答案 0 :(得分:7)
您的同事正在尝试编写编译器的工作而不是信任它,并且可能因此而感到悲观。 NRVO得到了很好的支持,如果函数是用值语义编写的,NRVO可以省略多个副本。绑定到引用将阻止这种情况,因为引用变量将不满足此优化的条件。 A simple test to demonstrate:
#include <iostream>
struct Test {
Test() { std::cout << "Created\n"; }
Test(const Test&) { std::cout << "Copied\n"; }
};
Test foo() {
Test t;
return t;
}
Test bar_good() {
const auto t = foo();
return t;
}
Test bar_bad() {
const auto& t = foo();
return t;
}
int main() {
const auto good = bar_good(); (void)good;
std::cout << "===========\n";
const auto& bad = bar_bad(); (void)bad;
}
给出了输出:
Created
===========
Created
Copied
一个对象在使用值语义时总计,但在使用引用时是一个冗余副本。根据复制(甚至移动)的扩展程度,您可以看到明显的性能差异。
答案 1 :(得分:1)
这是我的建议:
如果您需要对函数返回的对象进行只读访问,请使用
const auto& foo = function_1();
如果您希望能够修改返回的对象,或者不希望在您不知情的情况下将某个其他功能删除该对象,请复制。
auto foo = function_1();
使用g++ -std=c++11 -Wall
#include <iostream>
struct Foo
{
Foo() {}
Foo(Foo const& copy) { std::cout << "In copy constructor\n"; }
};
Foo function_1(){
Foo f;
return f;
}
int main()
{
std::cout << "-------------------------\n";
auto f1 = function_1();
std::cout << "-------------------------\n";
const auto& ref = function_1();
}
我得到以下输出(没有构造函数的输出)。
-------------------------
-------------------------
使用g++ -std=c++11 -Wall -fno-elide-constructors
我得到以下输出。
-------------------------
In copy constructor
In copy constructor
-------------------------
In copy constructor
如果编译器默认不支持复制省略,则在使用
时会生成其他副本auto f1 = function_1();
答案 2 :(得分:1)
恕我直言 - 当您希望返回的东西引用现有对象时,您应该通过引用返回。当您希望调用者获得与其他对象无关的副本时,您应该按值返回。
例如;如果您有一个函数从容器返回一个对象,并且您希望调用者能够更新原始值,则返回一个引用。如果调用者应该只使用自己的副本来处理容器中的原始对象,那么按值返回。
我的经验法则是值语义是最容易使用的 - 在返回对象和将它们作为参数时,所以我更喜欢按值取值/返回,除非我有明确的理由不这样做。
答案 3 :(得分:0)
参考的好处是它始终具有功能性。 如果按值捕获,则复制省略可以使其像通过引用捕获一样;但是如果删除了复制构造函数,则按值捕获会触发一次移动,然后销毁临时文件,效率会降低一些;如果删除了移动构造函数,则编译失败。 现在,必须在mutable和const之间做出选择:如果是可变的,那么你需要可变的右值引用;否则,const lvalue reference。