阅读“C++编程语言”(第4版)的“异常处理”一章中,有一个特定的临时清除代码帮助器示例:
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);
}
就像
auto act1 = finally([&]{ delete p; });
在声明act1的代码块的末尾运行lambda代码。
我认为这在Stroustrup进行测试时适用于Stroustrup,这是因为“返回值优化”将
Final_action<>
限制为单个实例-但是RVO是否只是可选的优化?如果实例从最终返回时被复制,则显然~Final_action()
对两个拷贝都运行。换句话说,p
被删除两次。这种行为是否被标准中的某些行为所阻止,或者代码是否足够简单以使大多数编译器对其进行优化?
最佳答案
实际上,该示例依赖于复制省略号,从C++ 17开始(在某些情况下)仅能保证复制。
话虽如此,复制省略是一种在大多数现代C++ 11/C++ 14编译器中实现的优化。如果该代码段在优化的构建中失败,我会感到惊讶。
但是,如果您想使其防弹,则可以添加一个move构造函数:
template<typename F>
struct Final_action {
Final_action(F f): clean{f} {}
~Final_action() { if (!moved) clean(); }
Final_action(Final_action&& o) : clean(std::move(o.clean)) {o.moved = true;}
private:
F clean;
bool moved{false};
};
template<class F>
Final_action<F> finally(F f)
{
return Final_action<F>(f);
}
不过,我认为这不是必需的。实际上,即使您不启用优化,大多数编译器也会复制省略号。 gcc , clang , icc 和 MSVC 都是示例。这是因为该标准明确允许复制省略。
如果添加此代码段:
int main() {
int i=0;
{
auto f = finally([&]{++i;});
}
return i;
}
并分析在godbolt.org上生成的程序集输出,您会看到
Final_action::~Final_action()
通常仅被调用一次(在main()
上)。启用优化后,编译器将更加进取:仅启用-O1
即可查看gcc 4.7.1的输出:main:
mov eax, 1 # since i is incremented only once, the return value is 1.
ret
关于c++ - 依靠RVO实现最终功能,我们在Stack Overflow上找到一个类似的问题:https://stackoverflow.com/questions/49632756/