【问题标题】:Mandatory copy elision? gcc 5.4.1强制复制省略? gcc 5.4.1
【发布时间】:2017-01-04 23:33:49
【问题描述】:

我目前正在努力使用 c++ 和 copy elision,特别是“命名返回值优化”(NRVO),以便能够实现工厂函数模式。我无法在不同的编译器中获得一致的行为。我的mwe:

#include <iostream>
struct base {
  virtual ~base() { std::cout << "dtor base\n"; }
};
struct derived : public base {
  ~derived() { std::cout << "dtor derived\n"; }
};
derived f() { return derived(); }
int main(int argc, char *argv[]) {
  std::cout << "start\n";
  new derived(f());
  std::cout << "done. should have leaked!\n";
}

注意:删除 virtual base-dtor 可以解决问题,但我需要它来实际实现。

在 gcc 5.4.0 的情况下,调用 dtor,不执行复制省略:

$ g++ test2.cpp && ./a.out
start
dtor derived
dtor base
done. should have leaked!

当使用 gcc 5.4.1(Ubuntu 将其称为 5.4.1,我假设这是 svn-head)时,我可以使用的所有 clang 以及各种其他 gcc 都会执行省略并成功泄漏内存:

$ g++ test2.cpp  && ./a.out
start
done. should have leaked!

当我阅读 internetz 上的不同地方时,编译器允许进行复制省略,但不是必需的。只有 c++17 引入了保证复制 elision。那么这是 gcc 5.4.0 中的错误,还是只是以不同的方式实现标准?

【问题讨论】:

  • 如果允许编译器优化它,但它不是必须的,它怎么可能是一个错误?
  • 我将编辑问题并删除有关 gcc 中健康代码的部分 - 这部分无论如何都会偏离主题,它只会破坏主题问题。
  • 当你说“成功泄露”时——你确实意识到 GCC 也在泄露内存? f() 返回的临时值被复制到 new 分配的内存中(使用复制构造函数),然后被销毁 - 这就是您看到的析构函数。
  • 尝试检测复制构造函数以查看发生了什么。
  • @NathanOliver:是的,这是我的问题。所以允许编译器不省略。嗯。

标签: c++ c++11 gcc


【解决方案1】:

在 C++17 之前,复制省略是一种可选优化,即使在 C++17 中,它也仅在某些情况下是强制性的。据我记得,即使在 C++17 中,(N)RVO 复制省略也不是强制性的——唯一强制复制省略的情况是临时初始化。

(N)RVO 不应该改变您的程序的行为,也不应该要求您的程序正常运行。你应该以这样一种方式编写你的代码,无论 (N)RVO 是什么,它都能正常工作,并且当/如果 (N)RVO 启动时工作得更快。

【讨论】:

  • 当我阅读this 时,复制省略可以改变程序行为:它是唯一可以省略(哈哈!)as-if 规则的优化形式 - 复制省略可以即使复制/移动对象有副作用,也可以应用。
  • @fdgsydfgsdfgsdfg 澄清一下,如果程序依赖于临时对象和副作用的创建,(N)RVO 可以改变程序的行为。 SergeyA 的观点是你的程序不应该依赖于这样的副作用,如果它不依赖,(N)RVO 将不会改变行为。
  • 是的,依赖复制省略是个坏主意……我明白了 ;-)
【解决方案2】:

在 gcc 5.4.0 的情况下调用 dtor,不执行复制省略:

start
dtor derived
dtor base
done. should have leaked!

实际上,复制省略已执行。否则你会看到

start
dtor derived
dtor base
dtor derived
dtor base
done. should have leaked!

这里有两个复制省略的机会。一个是f 中的RVO(注意它不是NRVO,只是普通的未命名RVO),另一个是从临时复制构造*derived。 GCC 5.4.0 执行了一种可能的复制省略。

all ...执行省略并成功泄漏内存

GCC 5.4.0 也成功泄露了derived 指向的内存。其他编译器从未创建过 5.4.0 所做并随后销毁的临时文件。

当我在 internetz 上阅读不同的地方时,允许编译器进行复制省略但不是必需的

正确。

这是 gcc 5.4.0 中的一个错误

没有。不总是实现复制省略的编译器是符合标准的。请参阅您问题中我之前引用的突出显示部分。

【讨论】:

  • 省略了:你是对的。使用-fno-elide-constructors 调用 gcc 会产生两倍的消息...哈...
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2021-02-07
  • 1970-01-01
相关资源
最近更新 更多