【问题标题】:Conditions for copy elision?复制省略的条件?
【发布时间】:2011-06-17 17:55:15
【问题描述】:

我想验证以下优化是否按预期工作:

  • RVO
  • 命名为 RVO
  • 按值传递参数时复制省略

所以我写了这个小程序:

#include <algorithm>
#include <cstddef>
#include <iostream>
#include <vector>

struct Foo {
    Foo(std::size_t length, char value) : data(length, value) { }

    Foo(const Foo & rhs) : data(rhs.data) { std::cout << "*** COPY ***" << std::endl; }

    Foo & operator= (Foo rhs) {
        std::cout << "*** ASSIGNMENT ***" << std::endl;
        std::swap(data, rhs.data); // probably expensive, ignore this please
        return *this;
    }

    ~Foo() { }

    std::vector<char> data;
};

Foo TestRVO() { return Foo(512, 'r'); }

Foo TestNamedRVO() { Foo result(512, 'n'); return result; }

void PassByValue(Foo inFoo) {}

int main()
{
    std::cout << "\nTest RVO: " << std::endl;
    Foo rvo = TestRVO();

    std::cout << "\nTest named RVO: " << std::endl;
    Foo named_rvo = TestNamedRVO();

    std::cout << "\nTest PassByValue: " << std::endl;
    Foo foo(512, 'a');
    PassByValue(foo);

    std::cout << "\nTest assignment: " << std::endl;
    Foo f(512, 'f');
    Foo g(512, 'g');
    f = g;
}

我在启用优化的情况下编译它:

$ g++ -o test -O3 main.cpp ; ./test

这是输出:

Test RVO: 

Test named RVO: 

Test PassByValue: 
*** COPY ***

Test assignment: 
*** COPY ***
*** ASSIGNMENT ***

根据输出 RVO 和命名的 RVO 按预期工作。但是,赋值运算符和调用PassByValue时不执行复制省略。

用户定义的复制构造函数是否允许复制省略? (我知道标准明确允许 RVO,但我不知道按值传递时的复制省略。)有没有一种方法可以在不定义复制构造函数的情况下验证复制省略?

【问题讨论】:

  • 为了清楚起见,(N)RVO 复制省略。它们不是唯一的形式,但是说您的示例表明未执行复制省略是不准确的。
  • Cooy elision 通常允许用于所有临时对象,但不适用于命名或绑定到引用的对象。似乎 gcc 执行的正是允许的。
  • @Dennis Zickefoose 谢谢,我修正了文本。

标签: c++


【解决方案1】:

标准说(在第 12.8.15 段中):

复制操作的这种省略是 以下允许 情况(可以结合 消除多个副本):

  • 在一个 函数中的 return 语句 类返回类型,当表达式 是非易失性的名称 具有相同的自动对象 cv-unqualified 类型作为函数 返回类型,复制操作可以 通过构造自动省略 对象直接进入函数 返回值

  • 当临时类 尚未绑定到 a 的对象 参考(12.2)将被复制到 具有相同的类对象 cv-不合格类型,副本 操作可以省略 构建临时对象 直接进入目标 省略复制

这两种情况都不适用于此处,因此不允许省略。第一个很明显(没有回报)。第二个是不允许的,因为你传入的对象不是临时的。

请注意,您的代码仍然可以,因为无论如何您都必须创建副本。要删除该副本,您必须使用 C++0x 的移动语义。

【讨论】:

  • 在 C++11 中,节号变为 §12.8/31,还有 2 种情况允许复制/移动省略,但仅与异常处理有关。
【解决方案2】:

您使用复制构造函数的方式不能省略,因为复制的对象在调用后仍然存在。

如果你尝试这种方式,它可能会更好:

PassByValue(Foo(512, 'a')); 

所有优化都是允许的,但不是必需的,因此由每个编译器决定它可以做什么以及将做什么。

【讨论】:

  • 这里确实进行了优化。这个例子加上@Space_C0wb0y 的回答让我明白了。
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2021-02-07
相关资源
最近更新 更多