完美转发,为什么析构函数会被调用两次?

时间:2015-08-17 09:17:30

标签: c++ c++11

我试图创建一个模仿Python语句的函数但是我遇到了一些我不太了解的有趣行为。

使用以下程序:

#include <iostream>

struct foo {
  foo() { std::cout << "foo()" << std::endl; }
  ~foo() { std::cout << "~foo()" << std::endl; }
};

auto make_foo() -> foo {
  return {};
}

template <typename T, typename F>
auto with(T&& t, F&& fn) -> void {
  fn(std::forward<T>(t));
}

auto main() -> int {
  std::cout << "before" << std::endl;
  with(make_foo(), [](auto f) {
    std::cout << "during" << std::endl;
  });
  std::cout << "after" << std::endl;
}

当使用Xcode 6.3和-std=c++14提供的clang编译并运行时,我得到以下输出:

before
foo()
during
~foo()
~foo()
after

有人知道为什么我的输出中有两个~foo()

3 个答案:

答案 0 :(得分:7)

以下是两个对象:

  

with(make_foo(), [](auto f) {

      1^^^^^^^^^    2^^^^^^

make_foo()返回的对象和函数参数f

如果您通过引用传递(更改为auto&& f),那么您将只看到一个对象的证据。

没有创建消息,因为这是由复制/移动构造创建的,并且这些构造函数中没有任何输出。

请注意make_foo()内可能有更多对象,但您的编译器正在执行 copy elision

答案 1 :(得分:3)

您的析构函数调用似乎与构造函数调用相匹配,因为您没有跟踪复制/移动构造函数。如果我们像这样添加跟踪:

struct foo {
  foo() { std::cout << "foo()" << std::endl; }
  ~foo() { std::cout << "~foo()" << std::endl; }
  foo(const foo&) { std::cout << "foo(const foo&)" << std::endl; }
  foo(foo&&) { std::cout << "foo(foo&&)" << std::endl; }
};

我们的输出现在是:

before
foo()
foo(foo&&)
during
~foo()
~foo()
after

移动构造的原因是你的lambda按值获取其参数:

[](auto f) {
// ^^^^^^
    std::cout << "during" << std::endl;
}

如果您不想要副本,请参考const,甚至转发参考。

答案 2 :(得分:1)

这对我有用,它接受lambda函数参数中的r-reference以防止复制:

#include <iostream>

struct foo {
  foo() { std::cout << "foo()" << std::endl; }
  ~foo() { std::cout << "~foo()" << std::endl; }
};

auto make_foo() -> foo {
  return {};
}

template <typename T, typename F>
auto with(T&& t, F&& fn) -> void {
  fn(std::forward<T>(t));
}

auto main() -> int {
  std::cout << "before" << std::endl;
  with(make_foo(), [](auto&&) { // r-reference!
    std::cout << "during" << std::endl;
  });
  std::cout << "after" << std::endl;
}

新的改进输出:

before
foo()
during
~foo()
after