阅读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在测试它时是有效的,因为返回值优化将Final_action<>限制在一个实例中--但是RVO不是一个可选的优化吗?如果实例在最后返回时被复制,那么很明显,两个副本都会运行~Final_action()。换句话说,p被删除了两次。
这种行为是被标准中的某些东西所阻止,还是代码仅仅简单到大多数编译器可以对其进行优化?
发布于 2018-04-03 16:48:26
实际上,该示例依赖于复制ellision,这在C++17之后只得到了保证(在某些情况下)。
尽管如此,复制ellision是在大多数现代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);
}但我不认为那是必要的。事实上,即使不启用优化,大多数编译器也会复制ellision。gcc、clang、icc和MSVC都是这方面的例子。这是因为标准明确允许复制ellision。
如果添加这个片段:
int main() {
int i=0;
{
auto f = finally([&]{++i;});
}
return i;
}分析godbolt.org上生成的程序集输出,您将看到Final_action::~Final_action()通常只被调用一次(在main()上)。在启用优化的情况下,编译器甚至更具侵略性:查看gcc 4.7.1的输出,只启用-O1:
main:
mov eax, 1 # since i is incremented only once, the return value is 1.
ret发布于 2018-04-04 15:15:57
--这只是因为C++17!与C++11或C++14一起工作--由于已删除的复制构造函数,它失败了。因为C++17有强制执行RVO的情况,因此不需要复制/移动构造函数。
如果实例被复制..。
那么,不允许复制呢?
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;
};如果您已经在使用boost,也可以从boost::noncopyable派生。
#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; });
}https://stackoverflow.com/questions/49632756
复制相似问题