Relying on RVO for finally function
阅读" C ++编程语言"(第4版)的"异常处理"一章中,有一个特定的临时清除代码帮助器示例:
1 2 3 4 5 6 7 8 9 10 11 12 | 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); } |
就像
1 | auto act1 = finally([&]{ delete p; }); |
在声明act1的代码块的末尾运行lambda代码。
我认为这在Stroustrup进行测试时适用于Stroustrup,因为返回值优化将
这种行为是否被标准中的某些行为所阻止,或者代码是否足够简单以使大多数编译器对其进行优化?
实际上,该示例依赖于复制省略号,从C ++ 17开始(在某些情况下)仅能保证复制。
话虽如此,复制省略是一种在大多数现代C ++ 11 / C ++ 14编译器中实现的优化。如果该代码段在优化的构建中失败,我会感到惊讶。
但是,如果要使其防弹,则可以添加move构造函数:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 | 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都是此类示例。这是因为该标准明确允许复制省略。
如果添加此代码段:
1 2 3 4 5 6 7 | int main() { int i=0; { auto f = finally([&]{++i;}); } return i; } |
并在godbolt.org上分析生成的程序集输出,您将看到
1 2 3 | main: mov eax, 1 # since i is incremented only once, the return value is 1. ret |
自C ++ 17起这才有效!对于C ++ 11或C ++ 14,由于删除了副本构造函数,因此它失败。从C ++ 17开始,在某些情况下会强制执行RVO,因此不需要复制/移动构造函数。
If the instance is copied [..]
好吧,不允许复制吗?
1 2 3 4 5 6 7 8 | template<typename F> struct Final_action { Final_action(F f): clean{f} {} Final_action(Final_action const &) = delete; Final_action & operator=(Final_action const &) = delete; ~Final_action() { clean(); } F clean; }; |
或从
有关防止复制的进一步讨论。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 | #include <iostream> template<typename F> struct Final_action { Final_action(F f): clean{f} {} Final_action(Final_action const &) = delete; Final_action & operator=(Final_action const &) = delete; ~Final_action() { clean(); } F clean; }; template<class F> Final_action<F> finally(F f) { return Final_action<F>(f); } int main() { auto _ = finally([]() { std::cout <<"Bye" << std::endl; }); } |