完美转发,为什么析构函数被调用两次
Perfect forwarding, why does the destructor get called twice?
我试图使一个函数模仿Python的with语句,但我遇到了一些有趣的行为,我不太理解。
使用以下程序:
#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()
吗?
下面是两个对象:
with(make_foo(), [](auto f) {
1^^^^^^^^^ 2^^^^^^
有make_foo()
返回的对象和函数参数f
。
如果您通过引用传递(更改为auto&& f
),那么您将只看到一个对象的证据。
没有创建消息,因为这是通过复制/移动构造创建的,并且在这些构造函数中没有任何输出。
请注意,make_foo()
内部可能有更多的对象,但编译器正在执行复制省略。
你的析构函数调用似乎与构造函数调用不匹配,仅仅是因为你没有跟踪复制/移动构造函数。如果我们像这样添加跟踪:
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
move构造的原因是您的lambda按值接受其参数:
[](auto f) {
// ^^^^^^
std::cout << "during" << std::endl;
}
如果不需要复制,可以通过引用到const,甚至转发引用。
通过在lambda函数参数中接受r-引用来防止复制,这对我来说是有效的:
#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
相关文章:
- 从具有按值捕获的 lambda 移动构造 std::函数时,移动构造函数调用两次
- C++析构函数调用两次,堆栈分配的复合对象
- Qt插槽调用了两次
- 对于优化级别为 0 的 std::vector,析构函数被调用两次
- 为什么转换运算符调用复制构造函数两次,而等效函数只调用它一次
- 调用一个小函数两次(例如在if条件和主体中)比将结果存储在局部变量中更可取
- 为什么这个自定义分配器的析构函数在 GCC/MSVS 的 stdlib 中被调用两次
- 插槽调用了两次qt
- 调用某个回调函数两次会导致分段错误:Nan
- 基于 MFC 对话框的应用程序无法调用对话框两次
- 重载运算符 new(),为什么构造函数被调用两次?
- 当 reset() 被unique_ptr调用两次时会发生什么?
- 为什么在C 中超载邮政增量运算符两次调用构造函数
- 现代C++编译器是否能够避免在某些条件下两次调用常量函数
- 如果我对async_read进行两次调用,那么只有在处理完第一次调用之后,才会处理第二次调用,这是否安全
- 如何正确地将对象添加到向量,而无需两次调用析构函数
- boost::asio vs. libpcap:避免两次调用关闭
- 为什么 DNSServiceProcessResult 两次调用我的回调
- 在资源管理器左窗格上两次调用Windows 7外壳扩展dll Initialize方法
- 通过连续两次调用boost::asio::read来检索正确的数据