首页
学习
活动
专区
圈层
工具
发布
社区首页 >问答首页 >最终函数依赖于RVO

最终函数依赖于RVO
EN

Stack Overflow用户
提问于 2018-04-03 14:35:46
回答 2查看 282关注 0票数 9

阅读C++编程语言(第4版),在异常处理一章中,有一个特殊清理代码的示例助手:

代码语言:javascript
复制
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);
}

它就像

代码语言:javascript
复制
auto act1 = finally([&]{ delete p; });

在声明act1的块末尾运行lambda代码。

我想这对于Stroustrup在测试它时是有效的,因为返回值优化将Final_action<>限制在一个实例中--但是RVO不是一个可选的优化吗?如果实例在最后返回时被复制,那么很明显,两个副本都会运行~Final_action()。换句话说,p被删除了两次。

这种行为是被标准中的某些东西所阻止,还是代码仅仅简单到大多数编译器可以对其进行优化?

EN

回答 2

Stack Overflow用户

回答已采纳

发布于 2018-04-03 16:48:26

实际上,该示例依赖于复制ellision,这在C++17之后只得到了保证(在某些情况下)。

尽管如此,复制ellision是在大多数现代C++11/C++14编译器中实现的一种优化。如果这个片段在优化的构建中失败了,我会感到惊讶的。

但是,如果想使其防弹,只需添加一个move构造函数:

代码语言:javascript
复制
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。gccclangiccMSVC都是这方面的例子。这是因为标准明确允许复制ellision。

如果添加这个片段:

代码语言:javascript
复制
int main() {
    int i=0;
    {
        auto f = finally([&]{++i;});
    }
    return i;
}

分析godbolt.org上生成的程序集输出,您将看到Final_action::~Final_action()通常只被调用一次(在main()上)。在启用优化的情况下,编译器甚至更具侵略性:查看gcc 4.7.1的输出,只启用-O1

代码语言:javascript
复制
main:
  mov eax, 1 # since i is incremented only once, the return value is 1.
  ret
票数 2
EN

Stack Overflow用户

发布于 2018-04-04 15:15:57

--这只是因为C++17!与C++11或C++14一起工作--由于已删除的复制构造函数,它失败了。因为C++17有强制执行RVO的情况,因此不需要复制/移动构造函数。

如果实例被复制..。

那么,不允许复制呢?

代码语言:javascript
复制
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派生。

关于防止复制的进一步讨论。

代码语言:javascript
复制
#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; });
}
票数 0
EN
页面原文内容由Stack Overflow提供。腾讯云小微IT领域专用引擎提供翻译支持
原文链接:

https://stackoverflow.com/questions/49632756

复制
相关文章

相似问题

领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档