我应该将ref或副本分配给值返回函数吗?

时间:2018-04-30 19:22:30

标签: c++ reference return-value move-semantics return-value-optimization

我们有一堆值返回函数:

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 ++核心指南中找不到任何关于此的建议。我希望这不是一个有问题的问题。

4 个答案:

答案 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。