代码之家  ›  专栏  ›  技术社区  ›  Steve

如何检查C++拷贝

  •  4
  • Steve  · 技术社区  · 14 年前

    我在上浏览了这篇文章 copy ellision 在C++中,我在Boost库中看到了关于它的注释。这很吸引人,因为我喜欢我的功能看起来像

    verylargereturntype DoSomething(...)
    

    而不是

    void DoSomething(..., verylargereturntype& retval)
    

    我有两个问题要问

    1. 谷歌几乎没有关于这方面的文档,这是多么真实?
    2. 如何检查此优化是否实际正在发生?我认为这涉及到集会,但我们可以说这不是我的强项。如果有人能给出一个非常基本的例子来说明成功的埃里森是什么样子的,那将是非常有用的。

    我不会仅仅为了美化事物而使用抄袭修饰,但是如果我能保证它是有效的,那听起来很有用。

    7 回复  |  直到 14 年前
        1
  •  9
  •   Michael Burr    14 年前

    我认为这是一个非常常用的优化方法,因为:

    1. 编译器不难做到
    2. 这可能是一个巨大的收益
    3. 它是C++的一个领域,在优化变得普遍之前是一个常见的评论。

    如果你只是好奇,就调试一下 printf() 在复制构造函数中:

    class foo {
    public:
        foo(): x(0) {};
    
        foo(int x_) : x( x_) {};
    
        foo( foo const& other) : x( other.x) {
            printf( "copied a foo\n");
        };
    
        static foo foobar() {
            foo tmp( 2);
    
            return tmp;
        }
    
    
    private:
        int x;
    };
    
    
    
    int main()
    {
        foo myFoo;
    
        myFoo = foo::foobar();
    
        return 0;
    }
    

    当我运行一个未经优化的构建时,会打印出“复制一个foo”,但当我优化构建时,不会打印任何内容。

        2
  •  5
  •   msw    14 年前

    从你引用的文章:

    尽管标准从来都不要求省略副本,但是我测试过的每个编译器的最新版本今天都会执行这些优化。但是,即使您不愿意按值返回重量级对象,复制省略仍然应该改变您编写代码的方式。

    它更著名的是 Return Value Optimization

        3
  •  3
  •   Mark Ransom    14 年前

    唯一能确定的方法就是看一下装配图,但你问的问题是错的。您不需要知道编译器是否正在删除副本,除非它与程序时间有关。如果在复制构造函数中花费了太多时间,分析器应该很容易告诉您。

    可怜的人解决这个问题的方法是在复制构造函数中放置一个静态计数器,并尝试使用两种形式的函数。如果计数相同,您就成功地避免了复制。

        4
  •  2
  •   Billy ONeal IS4    14 年前

    谷歌“命名回报值优化”和“回报值优化”。事实上,现代编译器在许多情况下不会执行拷贝。

    您可以通过返回带有副作用的类型(如打印消息)来检查是否发生了这种情况。 Wikipedia 有一些很好的例子说明当RVO和/或NRVO生效时程序输出在哪里发生变化。

        5
  •  2
  •   Steve Jessop    14 年前

    其外观示例:

    #include <iostream>
    
    struct Foo {
        int a;
        Foo(int a) : a(a) {}
        Foo(const Foo &rhs) : a(rhs.a) { std::cout << "copying\n"; }
    };
    
    int main() {
        Foo f = Foo(1);
    }
    

    如果您没有看到输出,那么复制省略已经发生。这是从初始值设定项中删除副本。副本删除的另一个法律案例是返回值,并通过以下方式进行测试:

    Foo getFoo() {
        return Foo(1);
    }
    
    int main() {
        Foo f = getFoo();
    }
    

    或者更令人兴奋的是,对于指定的返回值:

    Foo getFoo() {
        Foo f(1);
        return f;
    }
    
    int main() {
        Foo f = getFoo();
    }
    

    G++在没有优化标志的情况下为我执行所有这些省略,但是您不知道更复杂的代码是否会胜过编译器。

    请注意,复制省略对分配没有帮助,因此以下操作将始终导致调用 operator= 如果那个操作员打印了什么:

    Foo f(1);
    f = getFoo();
    

    因此,按值返回仍然会导致“复制”,即使执行了复制构造函数省略。因此,对于大类的分块来说,它仍然是设计阶段的性能考虑因素。你不想写这样的代码,如果你的应用程序将大量时间花在复制上,那么以后再修改它将是一件大事,而这本来是可以避免的。

        6
  •  1
  •   quamrana Ryuzaki L    14 年前

    要回答问题2,您可以编写一个演示程序,在其中编写 class DemoReturnType; 它检测了刚刚写入的构造函数和析构函数 cout 当他们被召唤时。这应该为您提供足够的关于编译器能力的信息。

        7
  •  1
  •   Puppy    14 年前

    R值引用在C++ 0x中解决了这个问题。您是否可以获得一个启用R值的编译器是另一个问题。上次我只检查VisualStudio 2010支持它。