为什么在我尝试返回引用时仍然复制对象

时间:2018-10-15 06:03:33

标签: c++ c++11 pass-by-reference auto lvalue

class Obj {
public:
    Obj(int aa, int bb): a(aa), b(bb) {}
    Obj(const Obj& o) {a = o.a; b = o.b;std::cout << "copying" << std::endl;}
    Obj(Obj&& o) {a = o.a; b = o.b;std::cout << "moving" << std::endl;}
    int a;
    int b;
};
const Obj& Min(const Obj &o1, const Obj &o2) {
    if (o1.a > o2.a) {
        return o1;
    } else {
        return o2;
    }
}
int main() {
    using namespace std;

    auto o1 = Obj(1,1);
    auto o2 = Obj(2,2);
    auto res = Min(o1, o2);

    cout << res.a << endl;
    res.a = 100;
    cout << o1.a << endl;
    cout << o2.a << endl;

    return 0;
}

程序仍然打印一个单词copying,表示激活了副本构造函数。那么在哪里调用构造函数?为什么函数不返回对o1的引用,以便修改res的值也会改变o1的值?

2 个答案:

答案 0 :(得分:8)

复制是在语句中完成的:

auto res = Min(o1, o2);

Min()的返回类型为const Obj&。上面的auto将推导为Obj,而不是const Obj&(即res类型将为Obj)。 res是对象,它是通过复制构造函数(即Obj::Obj(const Obj&))初始化的,因此将进行复制构造。

如果您改为写:

auto& res = Min(o1, o2)

res将是const Obj&类型,并且在那里不会进行复制构造,因为res将是引用,而不是对象。

答案 1 :(得分:5)

这与auto推导类型有关:

摘自CPP工作草案(N4713):

  

10.1.7.4.1占位符类型扣除[dcl.type.auto.deduct]
  ...
  4.如果占位符是自动类型说明符,则使用模板参数推导规则确定推导的类型T'替换T。

并且:

  

17.9.2.1从函数调用[temp.deduct.call]中推导模板参数
  ...
  2.如果P不是参考类型:
  ...
    (2.3)—如果A是具有简历资格的类型,则将类型A的顶级cv限定词用于类型推导。

     
      
  1. 如果P是引用类型,则将P引用的类型用于类型推导。
  2.   

所以下面的语句中的

auto res = Min(o1, o2);

res推导出为Obj,从而在分配时调用复制构造函数。

因此将以上内容修改为此:

auto& res = Min(o1, o2);

将使auto推论resconst Obj&

但是,如果您执行此操作,则res不能被修改,因为它是const的引用