我试图创建一个模仿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()
?
答案 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