在callsite参数中创建的临时对象的生命范围

时间:2016-09-18 19:07:56

标签: c++

我有以下代码,想知道何时调用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

它表明在完成后调用Foo的析构函数。我的gcc是4.8.3。

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

1 个答案:

答案 0 :(得分:1)

这是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)   在使用f()的复制构造函数将其传递给X之前;   或者,X(2)可能会在用于保留的空间中构建   论证

这意味着,这个:

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

int main() {
    go(Foo());
}

有时为&#34;表演者&#34;如你所愿!看,C ++逐渐到达那里;-)。

但是你知道,使用std::move会禁止这种行为,因为std::move会从materialized对象产生xvalue表达式:

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

int main() {
    go(std::move(Foo()));
}

总之,

  1. 在您的案例中未使用std::move时,对象的创建时间为Live on Coliru

  2. 但是当你使用std::move时,它会被创建两次Live on Coliru,这是因为对象的具体化。阅读class.temp/2的完整段落,了解具体化的含义。