这是来自Stroustrup的"C++编程语言"的代码,该代码实现了一个finally我无法理解析构函数被调用的地方.
template<typename F> struct Final_action
{
Final_action(F f): clean{f} {}
~Final_action() { clean(); }
F clean;
}
template<class F>
Final_action<F> finally(F f)
{
return Final_action<F>(f);
}
void test(){
int* p=new int{7};
auto act1 = finally( [&]{delete p;cout<<"Goodbye,cruel world\n";} );
}
Run Code Online (Sandbox Code Playgroud)
我有两个问题:
根据作者的说法,delete p只有一次被调用:当act1超出范围时.但是从我的理解:首先,act1将使用复制构造函数初始化,然后Final_action<F>(f)函数中的临时对象finally被破坏,delete p第一次调用,然后在函数结束test时第二次act1超出范围.我哪里弄错了?
为什么finally需要这个功能?我不能只定义Final_action act1([&]{delete p;cout<<"Goodbye,cruel world\n"})?那是一样的吗?
此外,如果有人能想到更好的标题,请修改当前的标题.
更新:在进一步思考之后,我现在确信析构函数可能会被调用三次.另外一个是用于在调用函数中自动生成的临时对象,void test()用作复制构造函数的参数act1.这可以通过-fno-elide-constructorsg ++中的选项进行验证.对于那些和我有同样问题的人,请参阅Bill Lynch在答案中指出的复制省略以及返回值优化.
Bil*_*nch 13
你是对的,这段代码坏了.它仅在应用返回值优化时才能正常工作.这一行:
auto act1 = finally([&]{delete p;cout<<"Goodbye,cruel world\n"})
Run Code Online (Sandbox Code Playgroud)
可能会也可能不会调用复制构造函数.如果是,那么你将有两个类型的对象,Final_action因此你将两次调用lambda.
最简单的解决方法是
template<typename F>
struct Final_action
{
Final_action(F f): clean{std::move(f)} {}
Final_action(const Final_action&) = delete;
void operator=(const Final_action&) = delete;
~Final_action() { clean(); }
F clean;
};
template<class F>
Final_action<F> finally(F f)
{
return { std::move(f) };
}
Run Code Online (Sandbox Code Playgroud)
并用作
auto&& act1 = finally( [&]{delete p;cout<<"Goodbye,cruel world\n";} );
Run Code Online (Sandbox Code Playgroud)
使用copy-list-initialization和生命周期扩展转发引用可以避免Final_action对象的任何复制/移动.复制列表初始化Final_action直接构造临时返回值,并且返回的临时值finally通过绑定延长其生命周期act1- 也无需任何复制或移动.
| 归档时间: |
|
| 查看次数: |
991 次 |
| 最近记录: |