如何为C+;中的函数返回的临时对象调用析构函数+;? 这是Stroustrup的“C++编程语言”的代码,它实现了最后的,我不能平静地理解析构函数调用的位置。 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";} ); } 模板结构最终\u操作 { 最后行动(F):清理{F}{} ~Final_action(){clean();} F清洁; } 模板 最终行动最终(F) { 返回最终行动(f); } 无效测试(){ int*p=新的int{7}; auto act1=finally([&]{delete p;cout

如何为C+;中的函数返回的临时对象调用析构函数+;? 这是Stroustrup的“C++编程语言”的代码,它实现了最后的,我不能平静地理解析构函数调用的位置。 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";} ); } 模板结构最终\u操作 { 最后行动(F):清理{F}{} ~Final_action(){clean();} F清洁; } 模板 最终行动最终(F) { 返回最终行动(f); } 无效测试(){ int*p=新的int{7}; auto act1=finally([&]{delete p;cout,c++,raii,C++,Raii,您是正确的,此代码已损坏。它仅在应用时正确工作。此行: auto act1 = finally([&]{delete p;cout<<"Goodbye,cruel world\n"}) auto-act1=finally([&]{delete p;cout代码被破坏。SimonKraemer提到的修订代码也被破坏了–它没有编译(finally中的return语句是非法的,因为Final_action既不可复制也不可移动)。仅使用生成的移动构造函数进行Final\u acti

您是正确的,此代码已损坏。它仅在应用时正确工作。此行:

auto act1 = finally([&]{delete p;cout<<"Goodbye,cruel world\n"})

auto-act1=finally([&]{delete p;cout代码被破坏。SimonKraemer提到的修订代码也被破坏了–它没有编译(finally
中的return语句是非法的,因为
Final_action
既不可复制也不可移动)。仅使用生成的移动构造函数进行
Final\u action
移动也不起作用,因为
F
保证有一个移动构造函数(如果没有,那么
Final\u action
生成的移动构造函数将静默地使用
F
的复制构造函数作为后备)移动后,
F
也不能保证为不可操作。事实上,示例中的lambda不会变成不可操作

有一个相对简单且便于携带的解决方案:

将标志
bool valid=true;
添加到
Final_action
并覆盖move c'tor和move assignment以清除源对象中的标志。仅在
valid
时调用
clean()
。这会阻止复制c'tor和复制赋值的生成,因此无需显式删除它们。(优点:将标志放入可重用的仅移动包装器中,这样您就不必执行
最终\u操作的移动任务和移动分配。在这种情况下,您也不需要显式删除。)

或者,删除
Final_action
的模板参数,并将其改为使用
std::function
。在调用它之前,检查
clean
是否为空。添加move c'tor和move赋值,将原始
std::function
设置为
nullptr
。(是的,这必须是可移植的。移动
std::function
并不保证源文件是空的。)优点:类型擦除的常见好处,例如能够将作用域保护返回到外部堆栈帧而不暴露
F
。缺点:可能会增加大量的运行时开销

在我当前的工作项目中,我基本上结合了这两种方法,使用类型擦除函数对象使用
ScopeGuard
AnyScopeGuard
。前者使用
boost::optional
并可以转换为后者。作为允许范围保护为空的额外好处,我可以显式地
discover()
它们也是。这允许使用范围保护设置事务的回滚部分,然后在提交时将其解除(使用非抛出代码)

更新:Stroustrup的新示例甚至没有编译。我没有注意到显式删除复制任务也会禁用移动任务的生成。

最简单的修复方法是

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) };
}
模板
结构最终行动
{
最后行动(F):清理{std::move(F)}{
最终行动(持续最终行动&)=删除;
void操作符=(const Final_action&)=删除;
~Final_action(){clean();}
F清洁;
};
模板
最终行动最终(F)
{
返回{std::move(f)};
}
并用作

auto&& act1 = finally( [&]{delete p;cout<<"Goodbye,cruel world\n";} );

auto&&act1=finally([&]{delete p;coutOne question per question,please.re#2我不明白为什么不可以。我猜有人痴迷于
auto
认为这更容易阅读。@lightness racesinorbit模板参数推断。由于lambda的原因,你实际上无法编写
最终动作的类型。请再次阅读本章。复制构造函数和
operator=
都设置为delete,以防止复制
Final\u action
。此外,示例代码在
cout
@btshengsheng lambda表达式创建函数对象(functor)后至少漏掉一个分号,是由编译器生成的一种类型。程序员不知道该类型的名称,但除此之外,它是一个带有
运算符()
的常规类。可能有人说“这就是它在我的系统上的工作方式”,而对编写远程可移植代码也不太关心。或者甚至是错误的代码:(@Quentin:更简单的修复:
auto&
——延长临时文件的生存期以匹配
act1
,防止任何copying@BenVoigt这会起作用,但会留下地雷。看起来这一章得到了更新:vs@SimonKraemer更新的代码也不正确,请参见Arne Vogel的答案。Simo修订的代码nKraemer确实不会编译(我已经用g++进行了测试),因为正如你所说,当复制构造函数被显式声明(被删除)时,移动构造函数不会自动生成,请参阅。在阅读了你的第一段6次之后,我想我终于完全明白你的意思了:-)。我假设您在回答的第四行中漏掉了一个NOTE?顺便说一句,看到Stroustrup一次又一次地生成错误的代码是非常有趣的…谢谢。工作正常!对于那些可能想知道的人,return语句
return{std::move(f)}
中的大括号调用复制列表初始化,请参阅。