在调用点参数处创建的临时对象的生命周期



我有以下代码,想知道Foo的析构函数何时被调用。

#include <iostream>
class Foo {
public:
  Foo() {
  }
  ~Foo() {
    std::cout << "destruct" << std::endl;
  }
};
void go(Foo f) {
  std::cout << "go" << std::endl;
}
int main() {
  go(Foo());
  std::cout << "main" << std::endl;
  return 0;
}

如果我运行代码,我得到以下输出

go
destruct
main

显示在go完成后调用Foo的析构函数。我的gcc是4.8.3。

我认为Foo的临时对象应该在它被复制到go的参数后被删除。但事实并非如此,Foo只存在一个对象。这在编译器的实现方面是预期的还是未定义的?

这是c++标准允许的优化。

c++标准草案,[class]。temp/2]表示,我只引用相关部分;重点是我的):

一个临时对象的实体化通常被延迟为同样长的时间尽可能避免创建不必要的临时对象。…

的例子:

class X {
public:
  X(int);
  X(const X&);
  X& operator=(const X&);
  ~X();
};
class Y {
public:
  Y(int);
  Y(Y&&);
  ~Y();
};
X f(X);
Y g(Y);
void h() {
  X a(1);
  X b = f(X(2));
  Y c = g(Y(3));
  a = f(a);
}

X(2) 是在容纳 f()的参数和的空间中构造的Y(3) 是在g() 的参数空间中构造的。

以前,在n3690中,它说:

一个实现可以使用一个临时变量来构造X(2)然后使用X的复制构造函数将其传递给f();或者,X(2)可以在用于容纳的空间中构造参数

意思是:

void go(Foo) {
    std::cout << "go" << std::endl;
}
int main() {
    go(Foo());
}

有时的"性能",因为你想要的!看到了吧,c++正在逐渐走向那个方向;-)。


但是你看,使用std::move会抑制这种行为,因为std::move从物化对象产生xvalue表达式:

void go(Foo) {
    std::cout << "go" << std::endl;
}
int main() {
    go(std::move(Foo()));
}
最后,

  1. 当你不使用std::move的情况下,对象被创建一次,看到Coliru

  2. 但是当你使用std::move时,它会被创建两次,就像在Coliru上看到的那样,这是因为对象的物化。阅读完整的课堂段落。

最新更新